JavaScript中的竞争条件与复合赋值

我不是在谈论涉及networking或事件的复杂的竞争条件。 相反,我似乎已经发现+=运算符在V8(Chrome 58或者Node 8)中不是primefaces的。

下面的代码旨在并行运行两个所谓的线程。 每个“线程”重复调用一个函数,该函数在睡眠几秒后返回其数字参数。 结果总结成一个累加器。

 function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Return the passed number after sleeping that many seconds async function n(c) { await sleep(c * 1000); console.log('End', c); return c; } let acc = 0; // global // Call n repeatedly and sum up results async function nForever(c) { while (1) { console.log('Calling', c); acc += await n(c); // += not atomic?! console.log('Acc', acc); } } (async function() { // parallel repeated calls nForever(1); nForever(5.3); // .3 for sanity, to avoid overlap with 1 * 5 })(); 

问题是,约5秒后,我预计累加器为10.3(5次1 + 1次5.3)。 但是,这是5.3!

+ =不是原子的

这不是一个竞争条件,因为你显式地使用await产生执行。

该标准定义了诸如+=的复合赋值不是primefaces的:复合赋值的左边在右边之前求值。 [ 1 ]

因此,如果您的RHS以某种方式更改acc ,则这些更改将被覆盖。 最简单的例子:

 var n = 1; n += (function () { n = 2; return 0; })(); console.log(n); 

事实上,在将acc += await n(c)行replace为:

 const ret = await n(c); acc += ret; 

避免了比赛状况。

我的猜测是V8并没有优化acc += await n(c)n()结果的ADD在包含acc的内存位置上,而是将其扩展到acc = acc + await n(c); ,当nForever(5.3)第一次调用时, acc的初始值是0。

这对我来说是非直觉的,虽然不知道V8开发者会认为它是一个错误。