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

Posted

技术标签:

【中文标题】赋值运算符、映射和承诺。该代码有啥问题? Javascript【英文标题】:Assignment operator, map and promises. What's wrong with that code ? Javascript赋值运算符、映射和承诺。该代码有什么问题? Javascript 【发布时间】:2020-06-19 22:06:04 【问题描述】:

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

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 完成

我第一次期待的。

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

【问题讨论】:

【参考方案1】:

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

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

在数组被迭代之后,但awaits 已经解析之前,count 的“当前”值被+= 使用被检索before 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 的语义。其中+=AssignmentOperators 之一,语法如下:

LeftHandSideExpressionAssignmentOperatorAssignmentExpression

做:

    设 lref 为计算 LeftHandSideExpression 的结果。 让 lval 成为?获取值(lref)。 设 rref 为评估 AssignmentExpression 的结果。 让 rval 是 ?获取值(rref)。 让 op 为 @,其中 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()。虽然numfn内部被重新赋值,但它没有任何作用,因为num的值已经被外部+=检索到了。


使用你的工作代码,当你这样做时

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

这会将somePromise 的解析值放入nb 变量中,然后 count += nb; 将运行。这符合预期,因为用于+=count 的“当前”值是在Promise 解决后检索的,因此如果先前的迭代重新分配count,它将被成功纳入到下一次迭代为止。

【讨论】:

非常感谢您令人印象深刻的详细回复!我现在明白了

以上是关于赋值运算符、映射和承诺。该代码有啥问题? Javascript的主要内容,如果未能解决你的问题,请参考以下文章

R 赋值运算符 := 有啥用?

赋值运算符和复制构造函数有啥区别?

+=和=+ C赋值运算符有啥区别[重复]

loadu/lddqu 和赋值运算符有啥区别?

python中的两个赋值运算符有啥区别? [复制]

在 C 中评估赋值运算符的左操作数有啥意义?