Angular & async / await - javascript

I am using the following in my angular app:
let getName = greenlet( async username => {
let url = `https://api.github.com/users/${username}`
let res = await fetch(url)
let profile = await res.json()
return profile.name
})
console.log(await getName('developit'));
Angular seems to be changing this 'await' into 'yield':
How do i use this within my Angular5 application? Thanks.

Babel changes async/await in the browser into generator functions, which utilize the yield keyword. Yields only work when they are in the correct context; specifically the next stage will be ensuring that the yield appears inside a generator function (you can read more about generators, but basically they are identified by a * in the function signature, e.g. function *doSomething().
A generator function will not get created if you have not correctly managed your async keywords. Babel will convert that await into a generator, but because the outer function is not async, it won't create the generator wrapper, thus the error. Some IDEs will report this error before you compile, but that's what the error you are seeing means.
You mentioned that this console.log is sitting inside the ngOnInit lifecycle hook; that function call is not by itself asynchronous. You can decorate it with async, but I wouldn't recommend that (it works, but what does it actually mean?). Instead you can try calling an explicitly async function and just ignore the returned promise:
ngOnInit() {
this.someFunction();
}
async someFunction() {
console.log(await getName('developit'));
}
We'll assume for now that greenlet knows what to do with the async function it has been handed (does it resolve the promise?)

Related

how does this async/await work when it's not being called in async function

I have a promise stored in a const
const goToWork = new Promise((resolve, reject) => {
setTimeout(() => resolve('Go to Work'));
});
since calling goToWork() would error expression is not callable
I tried storing running it in an async function:
async function callPromise() {
return await goToWork;
}
now awaiting callPromise as such await callPromise() returns pending<Promise> which tempted me to store it in another async function as such:
async function executePromise() {
console.log(await callPromise());
}
and then ran it executePromise()
// output: Go To Work
my question is how come executePromise() didn't complain of not running in an async function ? how come it did not need to be "awaited"
I tried storing running it in an async function:
There really is no point at all this this function:
async function callPromise() {
return await goToWork;
}
It absolutely the same as:
function callPromise() {
return goToWork;
}
which is a pointless function that doesn't do anything useful. So, since you're making a function with no practical use, there must be some basic misunderstanding of async/await.
To start with, all async function return a promise - always. So, return await fn() is not useful. You may as well just do return fn(). Both return a promise with the same state.
now awaiting callPromise as such await callPromise() returns pending which tempted me to store it in another async function as such:
Yes, as I said above. All async functions return a promise.
and then ran it executePromise() // output: Go To Work
There should be no surprise here.
console.log(await callPromise());
This will output the result of await callPromise() which (if it doesn't reject) will output the resolved value of the promise that callPromise() returns.
my question is how come executePromise() didn't complain of not running in an async function ? how come it did not need to be "awaited"
Here's your executePromise function:
async function executePromise() {
console.log(await callPromise());
}
There's no problem with this function because the await is inside an async function. That follows the async/await rules.
When you call it like this:
executePromise()
That just runs the function and because that function is an async function, it will always return a promise. There is no rule that calling an async function requires using await on it or must be called from within another async function.
You can call it like this:
executePromise().then(...).catch(...)
Or, you can put it in a async function:
async someFunction() {
try {
await executePromise();
} catch(e) {
// got error
console.log(e);
}
}
Or, you can just call it without regard for the returned promise:
executePromise();
Calling it naked like this last one is not paying any attention to whether the returned promise resolves or rejects and is not paying any attention to any resolved value. But, it's legal to do this. It possibly sets you up for an unresolved rejection because there's no reject handler, but if you know that promise will never reject and you don't care when it resolves or what the resolved value is, then this is allowed.
my question is how come executePromise() didn't complain of not running in an async function?
But it did run in an async function: async function executePromise().
Keep in mind that await is syntactic sugar (more or less, see comments), and you can always turn it back into Promise.then(), where this:
const x = await Promise.resolve('foo')
console.log(x)
behaves similar to
Promise.resolve('foo').then(x => console.log(x))
This makes it easy to understand what is going on:
async function callPromise() {
return await goToWork;
}
is similar to
async function callPromise() {
return goToWork.then(x => x);
}
And
console.log(await callPromise());
can be thought of as
goToWork.then(x => x).then(x => console.log(x))
None of that needs to be awaited, but you can use await to make it more readable.

Module-scope variable refuses to be overwritten by an async function. Why?

The module-scope variable "output" refuses to be overwritten by the async function "retrieveTextWrapper", and I cannot figure out why. My objective is to output the text on StackOverFlow's homepage. retrieveTextWrapper successfully scrapes this information, but I can't seem to assign this content to the output variable. What am I doing wrong? How can I print the scraped information from the main() function?
Note: I am using electron version 3.0.4 because bypassing CORS is less of a pain on that version.
const {BrowserWindow, app} = require('electron')
output = "this should be overwritten by the retrieveTextWrapper method"
async function main(){
navigate();
win.openDevTools();
await win.webContents.once('dom-ready',retrieveTextWrapper);
console.log(output);
//prints "this should be overwritten by the retrieveTextWrapper method"
}
function navigate() {
win = new BrowserWindow({width:900,height:900});
win.loadURL(`https://stackoverflow.com/`);
}
function retrieveText(){
return `document.querySelector("*").innerText`;
}
async function retrieveTextWrapper(){
output = await win.webContents.executeJavaScript(retrieveText().replace("*", "#content"));
}
app.on('ready',main)
win.webContents.once() does not return a promise (since interfaces generally don't accept both callbacks and return a promise at the same time).
Therefore await doesn't wait for the asynchronous operation to complete. Therefore, you're looking at output before its value has been reassigned. await only does something useful when you await a promise that is connected to the asynchronous operation you're trying to wait for.
To confirm this timing issue, add a unique console.log() statement before and after the await win.webContents.once('dom-ready',retrieveTextWrapper); and inside of retrieveTextWrapper and then you can see the sequencing of these log messages.
Yep, everything changes as it should within retrieveTextWrapper function. And your explanation makes a lot of sense. However, is it possible to wait for the callback to finish (using some other syntax aside from await)? That way, I can use the updated value for other operations in the main function?
You have a couple options.
You could "promisify" win.webContents.once() so you could then use await with it.
You could put the callback inline and put the rest of your code in main inside that callback (a classic way of dealing with asynchronous operations).
Here's an example of promisifying win.webContents.once():
function waitForDomReady() {
return new Promise((resolve, reject) => {
// may want to check if document already has dom-ready and resolve immediately
win.webContents.once('dom-ready', resolve);
});
}
And, you could then use it like this:
async function main(){
navigate();
win.openDevTools();
await waitForDomReady();
await retrieveTextWrapper();
console.log(output);
}
This assumes that the code in retrieveTextWrapper that calls win.webContents.executeJavaScript() does actually return a promise when it's done. If not, you have to promisify that too.

What is the meaning of the `async` keyword?

I have been reading up on async/await in Node.js. I have learnt that the await keyword waits for a promise to be resolved, or throws an exception if it was rejected.
I have also learnt that every function that wants to use await needs to be marked async. However, what does it mean for a function to be marked async?
All the resources and blog posts I was able to find seem to explain await in great detail, but ignore the concept of an async function, or briefly gloss over it. For instance, this author puts it like this:
This makes the function return a Promise implicitly.
What does the async keyword really do? What does it mean for a function to implicitly return a Promise? What are the side effects other than being able to use await?
Alright, so from the answers I have received so far it's clear that it simply wraps the function's return value into a Promise, much like Promise.then would. That just leaves a new question though. Why does a function that uses await need to be async and thus return a Promise?
No matter what you actually return from your function, your async function will still return a Promise. If you return a Number, it actually returns a Promise that resolves to the Number your originally returned. This allows you to write synchronous "looking" code.
Rather than having to write out this:
function foo(){
return Promise.resolve("foo");
}
You can just write this:
async function foo(){
return "foo";
}
and foo() will automagically return a Promise that resolves to "foo".
In response to you comment:
Does it behave like Promise.then in the sense that if you already
return a Promise, it won't wrap it again?
await will peel the Promise until it gets to a value:
async function foo() {
return Promise.resolve(Promise.resolve(true));
}
async function bar() {
return true;
}
(async function () {
let tmp;
tmp = await foo();
console.log(tmp);
tmp = await bar();
console.log(tmp);
console.log("Done");
}());
/*
Prints:
true
true
Done
*/
Why is async needed?
Paraphrasing from #KevinB's comment.
await, just like yield in a generator, pauses the execution of that context until the Promise it's waiting on is no longer pending. This cannot happen in normal functions.
If a function is async but does not contain an await, the promise will be resolved immediately, and any callbacks will be ran on the next tick.
What does async do?
async is syntactic sugar for making your method chain Promise objects.
Take the following method for example:
async function myFunction(a)
{
if (a == 10)
{
await otherFunction();
}
return 10;
}
The JavaScript runtime you use might make more optimized code, but in its simplest it will be something along the lines:
function myFunction(a)
{
if (a === 10)
{
return otherFunction()
.then(() => myFunction_continuation());
}
else
{
return myFunction_continuation();
}
function myFunction_continuation()
{
return Promise.resolve(10);
}
}
For documentation on the Promise type I recommend checking out the Mozilla Developer Network page about the Promise type .
Why do you need to mark it async? Why not just use await?
Because your method needs to be split up into multiple "parts" for it to be able to have code execute after the method being awaited on. The example above has a single continuation, but there could be multiple.
The designers of JavaScript want to make it visible to the developer that the runtime is doing this "magic". But maybe the most important reason is that they don't want to break existing code using await as a variable name. They do this by making await a "contextual keyword". A "contextual keyword" is only a keyword in specific scenarios, in this case: when used inside a method marked as async:
function ABC()
{
var await = 10;
}
The above compiles. But if I add the async keyword to the function declaration it no longer does and throws an Uncaught SyntaxError: Unexpected reserved word.
Asynchronous Task Running
The Basic idea is to use a function marked with async instead of a generator and use await instead of yield when calling a function, such as:
(async function() {
let contents = await readFile('config.json');
doSomethingWith(contents);
console.log('Done');
});
The Async Keyword before function indicates that the function is meant to run in an asynchronous manner. The await keyword signals that the function call to readFile('config.json') should return a promise, and if it doesn't, the response should be wrapped in a promise.
The end result is that you can write asynchronous code as if it were synchronous without overhead of managing an iterator-based state machine.
Understanding ECMACSCRIPT 6 by Nicholas c. Zakas

Is it legitimate to omit the 'await' in some cases?

I am using async/await in several places in my code.
For example, if I have this function:
async function func(x) {
...
return y;
}
Then I always call it as follows:
async function func2(x) {
let y = await func(x);
...
}
I have noticed that in some cases, I can omit the await and the program will still run correctly, so I cannot quite figure out when I must use await and when I can drop it.
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
For example:
async function func2(x) {
...
return func(x); // instead of return await func(x);
}
Is this conclusion correct, or else, what am I missing here?
EDIT:
A small (but important) notion that has not been mentioned in any of the answers below, which I have just encountered and realized:
It is NOT "legitimate" to drop the await within a return statement, if the called function may throw an exception, and that statement is therefore executed inside a try block.
For example, removing the await in the code below is "dangerous":
async function func1() {
try {
return await func2();
}
catch (error) {
return something_else;
}
}
The reason is that the try block completes without an exception, and the Promise object returns "normally". In any function which calls the outer function, however, when this Promise object is "executed", the actual error will occur and an exception will be thrown. This exception will be handled successfully in the outer function only if await is used. Otherwise, that responsibility goes up, where an additional try/catch clause will be required.
If func is an async function then calling it with and without await has different effects.
async function func(x) {
return x;
}
let y = await func(1); // 1
let z = func(1) // Promise (resolves to 1)
It is always legitimate to omit the await keyword, but means you will have to handle the promises in the traditional style instead (defeating the point of async/await in the first place).
func(1).then(z => /* use z here */)
If your return statements use await then you can be sure that if it throws an error it can be caught inside your function, rather than by the code that calls it.
await just lets you to treat promises as values, when used inside an async function.
On the other hand, async works quite the opposite, it tags the function to return a promise, even if it happens to return a real, synchronous value (which sounds quite strange for an async function... but happens often when you have a function that either return a value or a promise based on conditions).
So:
I have concluded that it is "legitimate" to drop the await only directly within a return statement.
In the last return statement of an async function, you just are returning a Promise, either you are return actually a directly a promise, a real value, or a Promise-as-value with the await keyword.
So, is pretty redundant to use await in the return statement: you're using await to cast the promise to a value -in the context of that async execution-, but then the async tag of the function will treat that value as a promise.
So yes, is always safe to drop await in the last return statement.
PS: actually, await expects any thenable, i.e. an object that has a then property: it doesn't need a fully spec compliant Promise to work, afaik.
PS2: of course, you can always drop await keyword when invoking synchronous functions: it isn't needed at all.
An async function always returns a Promise.
So please keep in mind that these writing of an async function are all the same:
// tedious, sometimes necessary
async function foo() {
return new Promise((resolve) => resolve(1)))
}
// shorter
async function foo() {
return Promise.resolve(1)
}
// very concise but calling `foo` still returns a promise
async function foo() {
return 1 // yes this is still a promise
}
You call all of them via foo().then(console.log) to print 1. Or you could call them from another async function via await foo(), yet it is not always necessary to await the promise right away.
As pointed out by other answers, await resolves the promise to the actual return value statement on success (or will throw an exception on fail), whereas without await you get back only a pending promise instance that either might succeed or fail in the future.
Another use case of omitting (i.e.: being careful about its usage) await is that you might most likely want to parallelize tasks when writing async code. await can hinder you here.
Compare these two examples within the scope of an async function:
async function func() {
const foo = await tediousLongProcess("foo") // wait until promise is resolved
const bar = await tediousLongProcess("bar") // wait until promise is resolved
return Promise.resolve([foo, bar]) // Now the Promise of `func` is marked as a success. Keep in mind that `Promise.resolve` is not necessary, `return [foo, bar]` suffices. And also keep in mind that an async function *always* returns a Promise.
}
with:
async function func() {
promises = [tediousLongProcess("foo"), tediousLongProcess("bar")]
return Promise.all(promises) // returns a promise on success you have its values in order
}
The first will take significantly longer than the last one, as each await as the name implies will stop the execution until you resolve the first promise, then the next one.
In the second example, the Promise.all the promises will be pending at the same time and resolve whatever order, the result will then be ordered once all the promises have been resolved.
(The Bluebird promise library also provides a nice Bluebird.map function where you can define the concurrency as Promise.all might cripple your system.)
I only use await when want to work on the actual values. If I want just a promise, there is no need to await its values, and in some cases it may actually harm your code's performance.
I got a good answer above, here is just another explanation which has occurred to me.
Suppose I have this:
async function func(x) {
...
return y;
}
async function func2(x) {
...
return await func(x);
}
async function func3(x) {
let y = await func2(x);
...
}
The reason why I can safely remove the await in the return statement on func2, is that I already have an await when I call func2 in func3.
So essentially, in func3 above I have something like await await func(x).
Of course, there is no harm in that, so it's probably better to keep the await in order to ensure desired operation.

Selenium with async/await in JS, find and click on element

I'm trying to refactor my tests using Selenium webdriver and Mocha to ES7 with async/await functionality. I have got following piece of code:
await loginPage.loginAsAdmin()
/* THIS DOES NOT WORK */
//await layout.Elements.routePageButton().click()
/* THIS DOES WORK */
let a = await layout.Elements.routePageButton()
await a.click()
I don't understand why the particular does not work - I get:
TypeError: layout.Elements.routePageButton(...).click is not a function
Function before click method returns webElement, as you can see:
Layout:
routePageButton: async () => await findVisibleElement('#route_info a')
const findVisibleElement = utils.Methods.Element.findVisible
Method:
findVisible: async (cssSelector) => {
let elm = await driver().findElement(by.css(cssSelector))
return elm
}
The problem here is misunderstanding that await is a language keyword in ES2017 that allows you to block execution of the calling async function until a Promise returned by an invoked function resolves.
routePageButton() returns a Promise, and this is why the second syntax above works, as execution is blocked until the Promise resolves to a WebElement object.
However in the syntax you are using in the first example, the function that it is attempting to await on (click()) is never called, because a Promise does not have a click() function. Note that you have two awaits in your second syntax, but only one in your first.
To do what you are attempting to do in one line, you would have to do something like:
await (await layout.Elements.routePageButton()).click()

Categories