Javascript中的异步/等待

Javascript中的异步/等待,javascript,async-await,Javascript,Async Await,在运行代码之前,我一直认为我理解了async/await在Javascript中的工作原理: let flag = false; function test() { [1, 2, 3].map(async n => { console.log(`flag set at ${n} before if?`, flag); if (!flag) { const x = await a(n); // do something with x

在运行代码之前,我一直认为我理解了
async/await
在Javascript中的工作原理:

let flag = false;

function test() {
  [1, 2, 3].map(async n => {
    console.log(`flag set at ${n} before if?`, flag);
    if (!flag) {
      const x = await a(n);
      // do something with x
      flag = x;
      console.log('flag set!');
    }
    console.log(`flag set at ${n} after if?`, flag);
  });
}

function a(n) {
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      resolve(n);
    });
  });
}

test();
实际输出为:

flag set at 1 before if? false
flag set at 2 before if? false
flag set at 3 before if? false
flag set!
flag set at 1 after if? 1
flag set!
flag set at 2 after if? 2
flag set!
flag set at 3 after if? 3
这和我想的很不一样:

flag set at 1 before if? false
flag set!
flag set at 1 after if? 1
flag set at 2 before if? 1
flag set at 2 after if? 1
flag set at 3 before if? 1
flag set at 3 after if? 1
我想我需要接受教育。谢谢

更新: 谢谢你对地图的评论。当我将代码更改为以下代码时,它按预期工作:

let flag = false;

async function test() {
  for (const n of [1, 2, 3]) {
    console.log(`flag set at ${n} before if?`, flag);
    if (!flag) {
      const x = await a(n);
      // do something with x
      flag = x;
      console.log('flag set!');
    }
    console.log(`flag set at ${n} after if?`, flag);
  }
}

function a(n) {
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      resolve(n);
    });
  });
}

test();

wait
关键字确保当前方法线程在继续之前等待函数调用解析

这就是为什么每个
标志都设置了在日志之后的
之前打印

但是,您的执行被封装在
.map
中的异步箭头函数中。这意味着,即使箭头函数本身已暂停,
.map
也不会暂停。正如@zerkms所提到的,它将你的阵列变成一个承诺阵列

将其视为对远程API的实际异步调用。您不需要等待服务器完成,来自
.map
的所有调用都将完成,然后在返回每个初始值时进行计算


这正是这里发生的事情。

map
并不等待,它只是将原始数组转换为承诺数组。对于顺序循环,请在
等待
周围使用
For of
map
只需调用回调函数三次,创建的三个承诺都是独立的。我很确定这与map的使用有关。如果要使用for循环,我认为不会有这种输出。@Andrew即使
setTimeout
立即调用回调,它也不是同步的,而是在宏任务循环中,因此,在所有promise事件之后肯定会延迟。在没有第二个参数的情况下调用setTimeout仍然会与没有setTimeout完全不同,因为它将被放在浏览器异步事件队列的底部!我并不是说这个答案是错误的,但它的某些部分看起来有误导性,比如“不管你的执行是如何封装在arrow函数中的”。作为一个箭头函数意味着增加了问题。你有没有建议如何更好地表达它,这样就不会产生误导?英语不是我的第一语言,所以我可能不会总是正确地使用单词:(我用@zerkms解释改写了这篇文章,希望能把事情弄清楚。