当等待表达式是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

之前,JavaScript引擎确实会获取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]);

在计时器超时后执行,并在执行该行时在右侧加载output2Current的值,如果发生计时器超时,则由上一个计时器超时更新。

将其与run1进行对比,在run1中,当开始计算赋值运算符右侧的表达式并对所有迭代使用相同的空数组值时,JavaScript引擎实质上缓存了ouput1的值,如代码片段中所示。

*副本的accepted answer表示在await返回右侧操作数之前,从存储器中检索加法运算的左侧操作数。在run1的情况下,我们看到,在确定用于调用方法的参数值之前,将对其调用的对象(output1的值)被检索。如链接答案的注释中所述,这是一个隐藏的陷阱。

相关文章