Reputation: 71
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?
Upvotes: 1
Views: 6150
Reputation: 21110
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');
}
Upvotes: 1
Reputation: 29282
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.
Upvotes: 5
Reputation: 72186
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.
Upvotes: 0
Reputation:
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".
Upvotes: 1