Await works fine on async function, but not on promises

Multi tool use
Await works fine on async function, but not on promises
I was playing with async/await and have found some strange discrepancy in the behavior, depending where await is called.
function test(msg) {
return new Promise(accept => {
console.log('in promise ', msg);
setTimeout(() => {
console.log('in timeout ', msg);
accept();
}, 1000)
});
}
async function run() {
await test('a');
await test('b');
}
run();
It outputs
in promise a
in timeout a
in promise B
in timeout B
, which is expected. However, if I move await inside "test" function, then the result is changed in a very unexpected manner:
async function test(msg) {
const promise = new Promise(accept => {
console.log('in promise ', msg);
setTimeout(() => {
console.log('in timeout ', msg);
accept();
}, 1000)
});
await promise;
}
function run() {
test('a');
test('B');
}
run();
Outputs
in promise a
in promise B
in timeout a
in timeout B
Could somebody please explain the behavior in the second case? Thank you.
await
await promise;
promise.then(() => {})
await
5 Answers
5
When you construct a Promise, the body of that Promise executes immediately. That's why in the second piece of code, the console.log
s inside of new Promise
before the setTimeout
occur right away.
console.log
new Promise
setTimeout
So why in the first example does it not do the same thing? It's because the second call to test
isn't called until the first completes. The two Promises are essentially chained together, as though you wrote this:
test
return test('a').then(() => test('B'));
If you had done this:
const promiseA = test('a');
const promiseB = test('B');
await Promise.all([promiseA, promiseB]);
...you'd get the same result as the second block of code.
Thanks for the answer. The thing I cannot understand is why in the 2nd case the 2nd promise is constructed before first "await" elapses? Await should pause the execution of "test('a')", therefore "test('B')" shouldn't be called at all... or not?
– stillwaiting
Jul 3 at 0:04
await
doesn't "pause" execution. It's just syntax for chaining things together. An async function becomes a function that returns a Promise
. Unless you await that Promise by the caller, it won't appear to "pause."– Jacob
Jul 3 at 0:06
await
Promise
(i.e., you still have to
await
the result of test(...)
; async/await doesn't make the asynchronous disappear, it just makes it more tolerable to deal with.)– Jacob
Jul 3 at 0:07
await
test(...)
All the await
ed Promises within an async
function are executed (to completion) sequentially.
await
async
In your first scenario, run
is the async
function, and its 2 await
calls to test
are executed sequentially.
run
async
await
test
In your second scenario, test
is the async
function, and its single await
operation has no effect whatsoever (since the async
function has no second await
operation that needs to "wait").
test
async
await
async
await
Your run
function is not async, so it just runs the two functions. async
/await
is just an alternate syntax for promises, so your second example is equivalent to this:
run
async
await
function test(msg) {
const promise = new Promise(accept => {
console.log('in promise ', msg);
setTimeout(() => {
console.log('in timeout ', msg);
accept();
}, 1000)
});
return promise.then(() => undefined);
}
function run() {
test('a');
test('B');
}
run();
To see clearly what happens you need to put another console.log('end')
at the end of your code (after run();
):
console.log('end')
run();
in promise a
end
in timeout a
in promise b
in timeout b
in promise a
in promise B
end
in timeout a
in timeout B
await
pauses the execution of the current function until the promise is resolved.
await
In the first example test('a')
is executed but then run()
is paused until the promise returned by test('a')
resolves, causing console.log('end')
to be executed.
test('a')
run()
test('a')
console.log('end')
In the second example test('a')
is partially executed, but is paused until the promise is resolved, so test('b')
is executed next, which is also paused, but then run()
is finished so console.log('end')
can be executed.
test('a')
test('b')
run()
console.log('end')
Hope this explanation helps!
In the fist example,
end
should be last line in the log, no? edit: Ah, console.log('end')
is literally the last line, not inside run
I guess.– Felix Kling
Jul 2 at 23:55
end
console.log('end')
run
You're right, I didn't want to copy/paste the code, it would make my answer very long
– Rodrigo Ferreira
Jul 2 at 23:59
These two code blocks are identical:
with await
await test('a');
test('B');
with chaining
test('a')
.then(() => {
test('B');
});
All you're doing with the await
is awaiting for the promise to resolve to move on. async
/await
is just syntactic sugar to avoid all the nesting with multiple promises
await
async
await
I think this is too vague. "test('B'); doesn't wait for test('a'); to complete before it starts" But you are also saying that
await
"pauses" execution.– Felix Kling
Jul 2 at 23:54
await
@FelixKling In the second example,
B
doesn't wait for a
to complete because await
is not used– Chris Deacy
Jul 2 at 23:57
B
a
await
I understand that. I'm just saying that "you're basically "pausing" execution of your function until that promise resolves." can be misinterpreted to "pause" all execution.
– Felix Kling
Jul 3 at 0:04
@FelixKling I can see where it could have been confusing. Updated it to be a bit more illustrative of my point
– Chris Deacy
Jul 3 at 0:16
Nice! Thank you!
– Felix Kling
Jul 3 at 0:16
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Keep in ind that
await
doesn't block execution. It's just allows you to write code dealing with promises differently.await promise;
in your example is actually completely useless. If you remove it or dopromise.then(() => {})
instead (which is basically whatawait
does) then you get the same result.– Felix Kling
Jul 2 at 23:48