当等待表达式是Conat()的参数时,为什么异步/等待有不同的输出?
我对下面的示例感到困惑。
我理解为什么output2
是[1000,2000,3000]
是因为闭包,这就是map()
中的所有异步函数更新相同数组output2
的原因。(如果我错了,请纠正我的概念。)
但是,我不明白为什么output1
是[3000]
。
run1
的行为不像run2
吗?你能告诉我有什么不同吗?
数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假">
"use strict";
function sleep(ms) {
return new Promise(resolve =>
setTimeout(() => {
resolve(ms);
}, ms)
);
}
const seconds = [1000, 3000, 2000];
let output1 = [];
let output2 = [];
(async function run1() {
await Promise.all(
seconds.map(async sec => {
output1 = output1.concat([await sleep(sec)]);
})
);
console.log({ output1 });
})();
(async function run2() {
await Promise.all(
seconds.map(async sec => {
const res = await sleep(sec);
output2 = output2.concat([res]);
})
);
console.log({ output2 });
})();
解决方案
查看语句
output1 = output1.concat([await sleep(sec)]);
左侧output1
是一个变量标识符,用于提供存储右侧求值结果的位置。变量的绑定不会更改,并且始终提供变量值的位置。
右侧output1
是一个值-从变量名称提供的位置检索的值。
现在,如果在继续求值之前,JavaScript引擎检索到output1
的值,则所有三个map函数调用都将
- 检索对
output
, 中存储的空数组的引用
- 等待计时器承诺,将
output1
设置为新值,即concat
方法返回的数组。
因此,每个映射操作将包含计时器值的数组连接到空数组,并将结果存储在output1
中,覆盖以前等待的操作的结果。
这解释了为什么当Promise.all
稳定时,您只能看到存储在output1
中的最后一个数组。我还将收回上面的";if Java Engine...";措辞。在await
:
output1
的值
数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假">
function sleep(ms) {
return new Promise(resolve =>
setTimeout(() => {
console.log( output1.length);
resolve(ms);
}, ms)
);
}
const seconds = [1000, 3000, 2000];
let output1 = [];
let output2 = [];
(async function run1() {
await Promise.all(
seconds.map(async sec => {
output1 = output1.concat([await sleep(sec)]);
//output1.push(await sleep(sec));
console.log(output1[0]);
})
);
console.log({ output1 });
})();
let count = 6;
let timer = setInterval( ()=> {
console.log(output1[0])
if(--count <=0 ) {
clearInterval( timer);
}
}, 500);
要澄清第二个方法(run2
)工作的原因(与是否存在闭包无关):
.map
方法同步调用map函数,同步调用返回承诺,而无需等待计时器承诺实现。
在第二个版本中
seconds.map(async sec => {
const res = await sleep(sec);
output2 = output2.concat([res]);
}
const res = await sleep( sec)
行保存执行上下文并等待sleep
承诺兑现。兑现承诺后,await
将恢复保存的上下文,并将承诺值存储在res
中。下一行
ouput2 = output2.concat([res]);
在计时器超时后执行,并在执行该行时在右侧加载output2
Current的值,如果发生计时器超时,则由上一个计时器超时更新。
将其与run1
进行对比,在run1
中,当开始计算赋值运算符右侧的表达式并对所有迭代使用相同的空数组值时,JavaScript引擎实质上缓存了ouput1
的值,如代码片段中所示。
await
返回右侧操作数之前,从存储器中检索加法运算的左侧操作数。在run1
的情况下,我们看到,在确定用于调用方法的参数值之前,将对其调用的对象(output1
的值)被检索。如链接答案的注释中所述,这是一个隐藏的陷阱。
相关文章