I know an async function returns a Promise, so I thought of replacing this code:
const hi = function (delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('hi');
resolve();
},delay)
});
};
const bye = async () => {
await hi(1000);
await hi(1000);
await hi(1000);
await hi(1000);
return 'bye';
};
bye().then((msg) => {
console.log(msg);
});
with this code:
const hi = async (delay) => {
setTimeout(() => {
console.log('hi');
},delay);
};
const bye = async () => {
await hi(1000);
await hi(1000);
await hi(1000);
await hi(1000);
return 'bye';
};
bye().then((msg) => {
console.log(msg);
});
The only difference is I'm using an async function instead of a function which returns a Promise, since an async function return a promise. But the second code doesn't work as I expected it too. It just console.logs bye immediately and then after 1s console.logs 'hi' 4 times. Can you please help me understand the reason behind this?
One major difference in both code examples is related to the return value of hi function.
In the first code example, function hi returns a promise whereas in the second code example, function hi does not explicitly returns any value; as a result, the promise returned by the async function is implicitly resolved with the value of undefined without waiting for the setTimeout's callback function to execute.
A promise object doesn't makes anything asynchronous; its just a wrapper around an already asynchronous operation and the promise object notifies whether the asynchronous operation completed successfully or whether it failed.
In the first code example, you have a wrapper promise around the setTimeout. That wrapper promise is not fulfilled until resolve is called from within the callback function of the setTimeout.
In the second code example, the promise returned by the async function isn't automatically bound to the asynchronous code inside the function.
After calling setTimeout, code execution reaches the end of the hi function; As there is no explicit return statement, promise returned by the async function is fulfilled with the value of undefined. This happens before the callback function of the setTimeout is called.
What's confusing you here is the setTimeout, your await hi( will actually await, but there isn't anything to wait for, that promise will almost immediately resolve and return so all 4 of them will execute almost instantaneously, then log "bye", then after 1 second your timeout will trigger and console.log "hi".
This answer adds some additional info to the one provided by Yousaf.
To shortly answer your comment:
Thanks for the explanation. Is there any way to write the hi function without using the 'Promise' keyword?
The answer is no. To convert a function using a callback interface to one that uses a promise you will have to manually create a promise somewhere. Functions using an older interface (usually) don't return promises, therefore you cannot await them. You can only await objects that are thenable (i.e. has a "then" method). If you await a non-thenable value or object, the await statement is essentially ignored.
MDN recommends wrapping callback methods at the lowest level possible:
Creating a Promise around an old callback API
A Promise can be created from scratch using its constructor.
This should be needed only to wrap old APIs.
In an ideal world, all asynchronous functions would already return
promises. Unfortunately, some APIs still expect success and/or failure
callbacks to be passed in the old way. The most obvious example is the
setTimeout() function:
setTimeout(() => saySomething("10 seconds passed"), 10*1000);
Mixing old-style callbacks and promises is problematic. If
saySomething() fails or contains a programming error, nothing
catches it. setTimeout is to blame for this.
Luckily we can wrap setTimeout in a promise. Best practice is to
wrap problematic functions at the lowest possible level, and then
never call them directly again:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10*1000).then(() => saySomething("10 seconds")).catch(failureCallback);
Basically, the promise constructor takes an executor function that
lets us resolve or reject a promise manually. Since setTimeout()
doesn't really fail, we left out reject in this case.
If you would apply this info to your situation, this means wrapping the setTimeout() call within a Promise constructor like shown above.
// The only responsibility of this function is converting the old
// callback interface, to a promise interface.
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Then use this new function in hi() instead of setTimeout().
async function hi(delay) {
await wait(delay);
console.log('hi');
}
A promise captures the result of an asynchronous processing that just started.
On the version of the code without promises, the asynchronous processing happens the same way as on the first version but there is nobody there waiting for it when it completes.
The second version of the hi() function is synchronous. It completes immediately. The async keyword does not have any effect on it. Also await hi() does not help, the function is still synchronous and it completes immediately.
Related
I've researched many many posts and articles, and documentation. I'm seeking deeper understanding.
From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
The await expression causes async function execution to pause until a Promise is settled, [...]
let fetchLocation = () => {
//fetches information from satellite system and
//returns a Vector2 of lat long on earth
return new Promise(resolve => {
setTimeout(() => {
resolve({
lat: 73,
long: -24
});
}, 2000);
});
}
//async functions automatically return a promise.
let main = async() => {
//awaits the promises resolution
let result = await fetchLocation();
console.log("I'm the awaited promise result", result)
//immediately returns a promise as promise pending
return result;
}
console.log(main().then(res => console.log("I'm the original promise, just passed along by the async function, it didn 't a-wait for me", res)))
Before going down this rabbit hole, I knew async/await was syntactical sugar to make async code look synchronous.
I therefore fully expected to see main return the lat long coordinates. Instead it returns a promise.
A couple questions.
Is the MDN documentation wrong? Does it not actually pause execution? It seems that the await keyword does not actually "pause" function execution... invoking main immediately returns a Promise<pending> object. However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop? (And an async return statement is still synchronous.)
So it seems that await unwraps a promise, and async wraps a promise, but you need to use await within async. I get that await might actually be promise.then() underneath the hood but if they want to make it fully synchronous in appearance, why not have the async function return the resolved promise value when used with await? Otherwise async await seems a bit redundant.
Is the MDN documentation wrong?
No
Does it not actually pause execution?
It pauses the execution of the async function, hands a Promise back to the calling function — because it hasn't reached the point in the async function where it knows what to return — then the execution of the calling function (and the rest of the code) continues.
However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop?
When the event loop stops being busy (running the code that called the async function in the first place and anything else that it picks up in the meantime) and the promise that was being awaited resolves, then the async function is woken up and given the resolved value of the function to continue processing.
why not have the async function return the resolved promise value when used with await?
Because that would block the entire event loop, which is why asynchronous logic (starting with callback style APIs) was introduced into JS in the first place.
Otherwise async await seems a bit redundant.
Consider a situation where you want to request a number of URLs from an API serially (so that you don't shove too many simultaneous requests at the API):
const data = [];
for (let i = 0; i < urls.length; i++) {
const response = await fetch(urls[i]);
const response_data = await response.json();
data.push(response_data);
}
If you were to rewrite that without await then you'd probably end up with something recursive to count your way through the loop. It would be significantly more complex.
async/await makes dealing with complex combinations of promises simple, and simple promises trivial.
knew async/await was syntactical sugar to make async code look synchronous.
And it does that inside the async function and only inside it.
It doesn't really pause the function, it just allows you to write the rest of the function code as if it were. But it's really just wrapping the rest of the code of the function into a .then().
E.g.
await foo = someFunc();
console.log(foo);
is executed like
someFunc().then((foo => {
console.log(foo);
});
Since it's really still asynchronous, you can't return the resolved value, because the original calling function returns immediately. But if the calling function is also declared async, it returns a Promise, and the caller can either use await again (so it appears to be synchronous) or use .then().
What is the difference between the two functions ?
function src() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(console.log("done! 1 ")), 4000);
});
}
async function src() {
setTimeout(() => console.log("done! 1 "), 4000);
}
The second one will return a Promise that resolves immediately.
The first one will return a Promise that resolves after 4 seconds.
In the first one, once the function is called a promise is returned with resolved state set in a timeout. So when the 4 sec timer ends (ofc without any errors) the resolved function triggers with text logged to the console.
Second one will too return a promise but of no certain use. On being called, it will just trigger the timer to print text after 4 sec. No promise functionality is used here.
Async/Await is just syntactic sugar for promises. Async/Await implicitly uses Promises. Async/Await just makes working with Promises easier.
That being said, your second functions doesn't make use of Async/Await properly. There's no await statement, but it's impossible to use an await statement directly with setTimeout anyway.
So, if you still want to use Async/Await you would have to write your own timeout function that would resemble your first function.
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function src() {
await timeout(4000);
console.log("done! 1 ");
}
Whenever you create new Promise you get to parameters resolve & reject. Whenever its success case you resolve on failure you reject. In case of resolve it will go to then & in case of reject it will go to catch. Promise is more over like real world promise (it could either be completed [resolved] or broken [reject]).
Though async & await helps you do asynchronous operations in JS. Whenever in async function it encounters await it will wait for that code to be executes and then will move to next line.
Refer to this video for better understanding along with example.
I am new to JS and was learning promises. So, let's say we have this code:
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
alert(result); // 1
return result * 2;
})
As you can see the code above, when promise is invoked, setTimeout is run via callback queue. The question is When setTimeOut is sent to a browser, will JS engine omit .then() and continues running the rest of the code until the promise resolves? Secondly, async/await example:
async function showAvatar() {
// read our JSON
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
// read github user
let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);
let githubUser = await githubResponse.json();
// show the avatar
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
// wait 3 seconds
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
img.remove();
return githubUser;
}
showAvatar();
When showAvatar function is called, JS engine will encounter let response = await fetch('/article/promise-chaining/user.json'); and sends fetch to the browser to handle. The second question is Will JS engine wait until fetch gets resolved or Will JS engine continue executing let user = await response.json(); and the rest of the code inside showAvatar function? If so, how can JS engine handle response.json() since response is not received? Hope you got my point))).
Your first example works like this:
new Promise runs, calling the function you pass it (the executor function) synchronously
Code in the executor function calls setTimeout, passing in a function to call 1000ms later; the browser adds that to its list of pending timer callbacks
new Promise returns the promise
then is called, adding the function you pass into it to the promise's list of fulfillment handlers and creating a new promise (which your code doesn't use, so it gets thrown away).
1000ms or so later, the browser queues a call to the setTimeout callback, which the JavaScript engine picks up and runs
The callback calls the resolve function to fulfill the promise with the value 1
That triggers the promise's fulfillment handlers (asynchronously, but it doesn't really matter for this example), so the handler attached in Step 4 gets called, showing the alert and then returning result * 2 (which is 1 * 2, which is 1). That value is used to fulfill the promise created and thrown away in Step 4.
Will JS engine wait until fetch gets resolved or Will JS engine continue executing let user = await response.json();...
It waits. The async function is suspended at the await in await fetch(/*...*/), waiting for the promise fetch returned to settle. While it's suspended, the main JavaScript thread can do other things. Later, when the promise settles, the function is resumed and either the fulfillment value is assigned to response (if the promise is fulfilled) or an exception will get thrown (if it is rejected).
More generally: async functions are synchronous up until the first await or return in their code. At that point, they return their promise, which is settled later based on the remainder of the async function's code.
In a comment you asked:
when async function is suspended at each await, will async function is removed from the call stack and is put back to the call stack again when the promise being awaited settles?
At a low level, yes; but to make debugging easier, a good, up-to-date JavaScript engine maintains an "async call stack" they use for error traces and such. For instance, if you run this on Chrome...
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function inner() {
await delay(80);
throw new Error("boom");
}
async function outer() {
await inner();
}
function wrapper() {
outer()
.then(result => {
console.log(result);
})
.catch(error => {
console.log(error.stack);
});
}
wrapper();
...the stack looks like this:
Error: boom
at inner (https://stacksnippets.net/js:18:11)
at async outer (https://stacksnippets.net/js:22:5)
Notice the "async" prior to "outer," and also notice that wrapper isn't mentioned anywhere. wrapper is done and has returned, but the async functions were suspended and resumed.
I was learning asynchrony in JS and came across Async/Await. Then, I came across this code:
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 2000);
});
}
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
msg(); // Message: 🤡 <-- after 2 seconds
Thus, I have questions on the above code. Firstly, how can async function msg() return message value if the function itself returns only undefined Promise, that is, the function does not explicitly use return keyword. Secondly, does await returns Promise or value itself having been unwrapped from Promise?
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 2000);
});
}
The above function is a function which returns a promise when it is called, but will only be resolved after 2 seconds.
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
the above function is asynchronous function which awaits for the promise to be resolved (in your case, after 2 seconds) and then only the console.log() triggers.
Note: any lines of code below the function msg() gets executed irrespective of the promise and the asynchronous function.
Running code snippet
function scaryClown() {
return new Promise(resolve => {
setTimeout(() => {
resolve('🤡');
}, 2000);
});
}
async function msg() {
console.log(scaryClown()); // try without await, so it will not wait for the result and prints first, but an unresolved promise
const msg = await scaryClown(); // actual result which is resolved, because we awaited
console.log('Message:', msg); // Prints once the resolved value is available.
}
msg(); //executes first but only prints the value once it is resolved.
console.log('I will continue executing irrespective of async func msg()');
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
await resolves promise and return resolved value. so above code with then can be written like below. hope this makes you understand async await.
function msg() {
scaryClown().then(msg => {
console.log('Message:', msg);
});
}
This is what happens:
When the async function (i.e. msg) is invoked, it creates a promise object (let's call it P), which it will return when it deals with an await expression. scaryClown() executes, which sets the timer and returns yet another promise object; let's call it Q. Then await is processed: at that moment the function returns the promise P. This promise is not resolved at that moment; it is pending.
If then there is any other code that followed the call of msg(), then that is executed like would be the case after any function call. Execution continues until the call stack is empty.
Then, some time later the timer event that was scheduled by scaryClown() will fire, and the corresponding promise Q resolves. Because this promise Q was in an await expression, this resolution triggers a restoration of the execution context of msg(), where the await expression is now evaluated to be Q's resolved value, i.e. "🤡".
The variable msg gets this value assigned, and it is displayed with console.log. Then the function completes, in this particular case without any explicit return. Remember that the function had already returned, so this is not really a normal return: code execution really ends here, as there is nothing on the call stack. This makes the promise P resolve. If there had been an explicit return statement, with a value, the promise P would have been resolved with that value. But since there is no such return statement in your example, promise P resolves with undefined.
As you don't do anything with the value returned by msg(), you don't really see what it returns. If you would have done:
msg().then(console.log);
... you would have seen undefined in the console.
Your questions:
how can async function msg() return message value if the function itself returns only undefined Promise, that is, the function does not explicitly use return keyword.
The msg() call does currently not provide you with the message value. Instead it uses console.log to output it, but that is not the value returned by msg(). The returned value is a promise. Even if it had an explicit return keyword, it would still not make msg() return something else than a promise. The only difference would be what value that promise would eventually resolve to.
does await returns Promise or value itself having been unwrapped from Promise?
await does two things at two different moments in time. First it makes the function return to the caller. Then, when the promise that it awaits resolves, it makes the function continue to run again, and await represents at that moment the promised value, which can therefore be assigned to a variable (or used in another way).
I personally don't use the verb "unwrapped" when talking about promises. But yes, the expression await promise evaluates (asynchronously) to the value that promise fulfils to.
I have a Vuex store and I am trying to fetch data from the Firebase Realtime Database. I am initially fetching the user information, however afterwards I would like to fetch some other information that relies upon the initial data fetched.
As you can see from the code, I am trying to do this using async / await, however whenever firing the two actions in my created() hook, the user's information isn't initialised, and therefore the second action fails.
My user store
async fetchCreds({ commit }) {
try {
firebase.auth().onAuthStateChanged(async function(user) {
const { uid } = user
const userDoc = await users.doc(uid).get()
return commit('SET_USER', userDoc.data())
})
} catch (error) {
console.log(error)
commit('SET_USER', {})
}
}
My club action which relies upon the above call
async fetchClubInformation({ commit, rootState }) {
try {
const clubIDForLoggedInUser = rootState.user.clubId
const clubDoc = await clubs.doc(clubIDForLoggedInUser).get()
return commit('SET_CLUB_INFO', clubDoc.data())
} catch (error) {
console.log(error)
}
}
}
The methods being called within my component's created() method.
created: async function() {
await this.fetchCreds();
await this.fetchClubInformation();
this.loading = false;
}
I have a feeling I'm fundamentally misunderstanding async / await, but I can't understand what in the code is incorrect - any help or advice would be greatly appreciated.
I'm not particularly familiar with Firebase but after a bit of digging through the source code I think I can shed a little light on your problems.
Firstly, consider the following example:
async function myFn (obj) {
obj.method(function () {
console.log('here 1')
})
console.log('here 2')
}
await myFn(x)
console.log('here 3')
Question: What order will you see the log messages?
Well here 2 will definitely come before here 3 but it's impossible to tell from the code above when here 1 will show up. It depends on what obj.method does with the function it's been passed. It might never call it at all. It might call it synchronously (e.g. Array's forEach method), in which case here 1 will appear before the other messages. If it's asynchronous (e.g. timers, server calls) then here 1 may not show up for some time, long after here 3.
The async modifier will implicitly return a Promise from the function if it doesn't return a Promise itself. The resolved value of that Promise will be the value returned from the function and the Promise will resolve at the point the function returns. For a function without a return at the end that's equivalent to it finishing with return undefined.
So, to stress the key point, the Promise returned by an async function will only wait until that function returns.
The method onAuthStateChanged calls its callback asynchronously, so the code in that callback won't run until after the surrounding function has completed. There's nothing to tell the implicitly returned Promise to wait for that callback to be invoked. The await inside the callback is irrelevant as that function hasn't even been called yet.
Firebase makes extensive use of Promises, so typically the solution would just be to return or await the relevant Promise:
// Note: This WON'T work, explanation follows
return firebase.auth().onAuthStateChanged(async function(user) {
// Note: This WON'T work, explanation follows
await firebase.auth().onAuthStateChanged(async function(user) {
This won't work here because onAuthStateChanged doesn't actually return a Promise, it returns an unsubscribe function.
You could, of course, create a new Promise yourself and 'fix' it that way. However, creating new Promises using new Promise is generally considered a code smell. Typically it's only necessary when wrapping code that doesn't support Promises properly. If we're working with a library that has proper Promise support (as we are here) then we shouldn't need to create any Promises.
So why doesn't onAuthStateChanged return a Promise?
Because it's a way of watching all sign-in/sign-out events. Every time the user signs in or signs out it'll call the callback. It isn't intended as a way to watch a particular sign-in. A Promise can only be resolved once, to a single value. So while a single sign-in event could be modelled with a Promise it's meaningless when watching all sign-in/sign-out events.
So fetchCreds is registering to be notified about all sign-in/sign-out events. It doesn't do anything with the returned unsubscribe function, so presumably it'll be listening to all such events until the page is reloaded. If you call fetchCreds multiple times it'll keep adding more and more listeners.
If you're waiting for a user to finish signing in then I suggest waiting for that directly instead. firebase.auth() has various methods starting with the prefix signIn, e.g. signInWithEmailAndPassword, and these do return a Promise that resolves when the user has finished signing in. The resolved value provides access to various information, including the user. I don't know which method you're using but the idea is much the same for all of them.
However, it might be that you're really just interested in grabbing the details of the current user. If that's all you want then you don't need to use onAuthStateChanged at all. You should just be able to grab a copy using the currentUser property. Something like this:
async fetchCreds({ commit }) {
try {
const { uid } = firebase.auth().currentUser
const userDoc = await users.doc(uid).get()
commit('SET_USER', userDoc.data())
} catch (error) {
console.log(error)
commit('SET_USER', {})
}
}
As I've already mentioned, this relies on the assumption that the user is already signed in. If that isn't a safe assumption then you might want to consider waiting until after sign in has completed before creating components that need user credentials.
Update:
Questions from the comments:
If the obj.method() call was asynchronous and we did await the callback function within it, would that ensure that the outer async function (myFn) never resolves before the inner one has finished?
I'm not entirely sure what you're asking here.
Just to be clear, I'm being very careful with my use of the words async and asynchronous. A function such as setTimeout would be considered asynchronous but it is not async.
async/await is just a lot of syntactic sugar around Promises. You don't really wait for a function, you wait for a Promise. When we talk about awaiting an async function we're really talking about waiting for the Promise it returns to resolve.
So when you say await the callback function it's not really clear what that means. Which Promise are you trying to await?
Putting the async modifier on a function doesn't make it magically wait for things. It will only wait when it encounters await. You can still have other asynchronous calls within an async function and, just like with a normal function, these calls will be performed after the function has returned. The only way to 'pause' is to await a Promise.
Putting an await inside another function, even a nested function, won't make any difference to whether the outer function waits unless the outer function is already waiting for the inner function. Behind the scenes this is all just Promises chaining then calls. Whenever you write await you're just adding another then call to a Promise. However, that won't have the desired effect unless that Promise is in the same chain as the Promise returned by the outer async function. It only needs one link to be missing for the chain to fail.
So modifying my earlier example:
async function myFn (obj) {
await obj.method(async function () {
await somePromise
// ...
})
// ...
}
await myFn(x)
Note that there are 3 functions here: myFn, method and the callback passed to method. The question is, will await myFn(x) wait for somePromise?
From the code above we can't actually tell. It would depend on what method does internally. For example, if method looked like this then it still wouldn't work:
function method (callback) {
setTimeout(callback, 1000)
}
Putting async on method won't help, that'll just make it return a Promise but the Promise still won't be waiting for the timer to fire.
Our Promise chain has a broken link. myFn and the callback are both creating their parts of the chain but unless method links those Promises together it won't work.
On the other hand, if method is written to return a suitable Promise that waits for the callback to complete then we will get our target behaviour:
function method (callback) {
return someServerCallThatReturnsAPromise().then(callback)
}
We could have used async/await here instead but there was no need as we can just return the Promise directly.
Also, if in the async myFn function you're not returning anything, does that mean it'll resolve immediately and as undefined?
The term immediately is not well-defined here.
If a function isn't returning anything at the end then it's equivalent to having return undefined at the end.
The Promise returned by an async function will resolve at the point the function returns.
The resolved value for the Promise will be the value returned.
So if you aren't returning anything it will resolve to undefined. Resolving won't happen until the end of the function is reached. If the function doesn't contain any await calls then this will happen 'immediately' in the same sense as a synchronous function returning 'immediately'.
However, await is just syntactic sugar around a then call, and then calls are always asynchronous. So while the Promise might resolve 'immediately' the await still has to wait. It's a very short wait, but it isn't synchronous and other code may get the opportunity to run in the meantime.
Consider the following:
const myFn = async function () {
console.log('here 3')
}
console.log('here 1')
Promise.resolve('hi').then(() => {
console.log('here 4')
})
console.log('here 2')
await myFn()
console.log('here 5')
The log messages will appear in the order they're numbered. So even though myFn resolves 'immediately' you'll still get here 4 jumping in between here 3 and here 5.
To make it short
fetchCreds({ commit }) {
return new Promise((resolve, reject) => {
try {
firebase.auth().onAuthStateChanged(async function(user) {
const { uid } = user
const userDoc = await users.doc(uid).get()
commit('SET_USER', userDoc.data())
resolve()
})
} catch (error) {
console.log(error)
commit('SET_USER', {})
resolve()
}}
}
async () => undefined // returns Promise<undefined> -> undefined resolves immediatly
asnyc () => func(cb) // returns Promise<any> resolves before callback got called
() => new Promise(resolve => func(() => resolve())) // resolves after callback got called