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

Assignment operator, map and promises. What's wrong with that code ? Javascript

我正在做一些事情,然后 运行 遇到了一个我无法理解的问题。 我简化了代码以获得:

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
Done

已记录。

当我改变

count += await somePromise();

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

我明白了

5
Done

这是我第一次期待的。

你能帮我找出问题所在吗?没看懂。

当解释器遇到 await 时,它会暂停函数直到 Promise 的解析。即使 Promise 立即 resolve,函数也只会在下一个微任务期间恢复。相反,数组通过 立即 同步迭代。当你这样做时

const promises = arr.map(async () => {
    count += await somePromise();
})

数组遍历后,但之前 awaits 已解析,count 的"current" 值被采用+= 的使用在 之前 被检索 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。

如果您对规范中对此的描述感兴趣,请查看 assignment operators 的语义。其中 +=AssignmentOperator 之一,语法如下:

LeftHandSideExpression AssignmentOperator AssignmentExpression

会:

  1. Let lref be the result of evaluating LeftHandSideExpression.
  2. Let lval be ? GetValue(lref).
  3. Let rref be the result of evaluating AssignmentExpression.
  4. Let rval be ? GetValue(rref).
  5. Let op be the @ where AssignmentOperator is @=.
  6. Let r be the result of applying op to lval and rval as if evaluating the expression 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 的 "current" 值在 Promise 解析后被检索,因此如果先前的迭代重新分配 count,下一次迭代将成功考虑它。