我在做一些事情时遇到了一个我无法理解的问题。我简化了代码,得到了:
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'));
你期待什么结果?
1
完成
已记录。
当我更改时
count += await somePromise();
至
const nb = await somePromise();
count += nb;
我得到
5
完成
我第一次想到的。
你能帮我找出毛病吗?我不明白。
当解释器遇到await
时,它将暂停函数,直到Promise得到解析。即使Promise立即解决,该功能也只能在下一个微任务期间恢复。相反,数组通过立即同步迭代。当你做
const promises = arr.map(async () => {
count += await somePromise();
})
在数组迭代通过之后,但在await
解析之前,+=
使用的count
的"当前"值在await
解析之前检索,在此之前count
的值为0。因此,在解释器看来,好像有5个单独的语句:
count += await somePromise();
count += await somePromise();
count += await somePromise();
count += await somePromise();
count += await somePromise();
解决类似的问题
const currentValueOfCount = count;
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
count = currentValueOfCount + await somePromise();
因此,每次,=
的右侧解析为0 + 1
,因此在循环结束时,count
仅为1。
如果您对规范中的描述感兴趣,请查看赋值运算符的语义。其中+=
是AssignmentOperator
之一,以下语法为:
LeftHandSideExpression AssignmentOperator AssignmentExpression
做:
- 设lref是计算LeftHandSideExpression的结果
- 让lval?GetValue(lref(
- 设rref为赋值AssignmentExpression的结果
- 让rval?GetValue(rref(
- 设op为@where AssignmentOperator为@=
- 设r是将op应用于lval和rval的结果,就好像评估表达式lval-op-rval一样
查看如何在评估运算符右侧之前立即检索lval
。(如果lval
是在之后检索的,则评估右侧的AssignmentExpression
,结果将为5,正如您所期望的那样(
下面是一个没有异步操作的行为示例:
let num = 5;
const fn = () => {
num += 3;
return 0;
}
num += 2 + fn();
console.log(num);
上面,num += 2 + fn();
立即检索num
作为5
以在+=
中使用,然后调用fn()
。虽然num
在fn
内部被重新分配,但它没有任何效果,因为num
的值已经被外部+=
检索到。
使用您的工作代码,当您执行时
const nb = await somePromise();
count += nb;
这将把somePromise
的解析值放入nb
变量中,然后运行count += nb;
。这与预期的一样,因为用于+=
的count
的"当前"值是在Promise解析后检索的,因此,如果上一次迭代重新分配了count
,则下一次迭代将成功地将其考虑在内。