赋值运算符、映射和承诺。该代码有什么问题?爪哇语



我在做一些事情时遇到了一个我无法理解的问题。我简化了代码,得到了:

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

做:

  1. 设lref是计算LeftHandSideExpression的结果
  2. 让lval?GetValue(lref(
  3. 设rref为赋值AssignmentExpression的结果
  4. 让rval?GetValue(rref(
  5. 设op为@where AssignmentOperator为@=
  6. 设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()。虽然numfn内部被重新分配,但它没有任何效果,因为num的值已经被外部+=检索到。


使用您的工作代码,当您执行时

const nb = await somePromise();
count += nb;

这将把somePromise的解析值放入nb变量中,然后运行count += nb;。这与预期的一样,因为用于+=count的"当前"值是在Promise解析后检索的,因此,如果上一次迭代重新分配了count,则下一次迭代将成功地将其考虑在内。

最新更新