I was doing some stuff and I ran into a problem that I can't understand. I simplified the code to get that:
function somePromise() {
return new Promise((resolve, reject) => {
resolve(1);
});
}
async function main() {
let count = 0;
const arr = [1, 2, 3, 4, 5];
const promises = arr.map(async () => {
count += await somePromise();
})
await Promise.all(promises);
console.log(count);
}
main().then(() => console.log('Done'));
What result do you expect ?
1
Done
is logged.
When I change
count += await somePromise();
to
const nb = await somePromise();
count += nb;
I get
5
Done
what I expected for the first time.
Can you help me to find what is wrong ? I don't get it.
Combining And Resolving all Promises with Promise. all() , map() and Async/Await. So the first promise will be resolved then all the promises returned from the getProductId() for each product and then finally all the promises returned from capitalizeId() for each ID returned from the previous promises.
The "Bad assignment" error (and the alternative "Invalid left-hand side in assignment" error) are thrown when JSLint, JSHint or ESLint encounters an assignment expression in which the left-hand side is a call expression.
The addition assignment operator ( += ) adds the value of the right operand to a variable and assigns the result to the variable.
The multiplication assignment operator ( *= ) multiplies a variable by the value of the right operand and assigns the result to the variable.
When the interpreter comes across the await
, it will pause the function until the resolution of the Promise. Even if the Promise resolves immediately, the function will only resume during the next microtask. In contrast, the array is iterated through immediately, synchronously. When you do
const promises = arr.map(async () => {
count += await somePromise();
})
After the array is iterated through, but before the await
s have resolved, the "current" value of count
which is taken for use by the +=
is retrieved before the await
resolves - and the value of count
before then is 0. So, it looks to the interpreter as if there are 5 separate statements:
count += await somePromise();
count += await somePromise();
count += await somePromise();
count += await somePromise();
count += await somePromise();
which resolve to something like
const currentValueOfCount = count;
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
So, each time, the right-hand side of the =
resolves to 0 + 1
, so at the end of the loop, count
is only 1.
If you're interested where this is described in the specification, look at the semantics for assignment operators. Where +=
is one of the AssignmentOperator
s, the following syntax:
LeftHandSideExpression AssignmentOperator AssignmentExpression
does:
- Let lref be the result of evaluating LeftHandSideExpression.
- Let lval be ? GetValue(lref).
- Let rref be the result of evaluating AssignmentExpression.
- Let rval be ? GetValue(rref).
- Let op be the @ where AssignmentOperator is @=.
- Let r be the result of applying op to lval and rval as if evaluating the expression lval op rval.
See how lval
is retrieved immediately, before the right-hand side of the operator is evaluated. (If lval
had been retrieved after the right-hand side, the AssignmentExpression
, is evaluated, the results would have been 5, as you're expecting)
Here's an example of this behavior without asynchronous operations:
let num = 5;
const fn = () => {
num += 3;
return 0;
}
num += 2 + fn();
console.log(num);
Above, the num += 2 + fn();
retrieves num
as 5
immediately for use in +=
, then calls fn()
. Although num
is reassigned inside fn
, it doesn't have any effect, because the value of num
has already been retrieved by the outer +=
.
With your working code, when you do
const nb = await somePromise();
count += nb;
This will put the resolve value of somePromise
into the nb
variable, and then count += nb;
will run. This behaves as expected because the "current" value of count
used for +=
is retrieved after the Promise resolves, so if a prior iteration reassigned count
, it'll be successfully taken into account by the next iteration.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With