JavaScript中的async/await

3

我原本以为自己理解了Javascript中async/await的工作原理,直到我运行了以下代码:

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();

5
map 不会等待,它只是将原始数组转换为一个 Promise 数组,不改变原意且保持通俗易懂。 - zerkms
1
对于顺序循环,请在await周围使用for ofmap只会调用回调函数三次,而每个创建的Promise都是独立的。 - Bergi
1
我非常确定这与使用map有关。如果你使用for循环,我认为你不会得到这个输出。 - Kevin Pastor
2
@Andrew 尽管setTimeout会立即调用回调函数,但它不是同步的,而是有一个最小值-并且它在宏任务循环中,所以肯定会被延迟到所有承诺完成后。 - Bergi
2
调用setTimeout而没有第二个参数仍然与根本没有setTimeout不同,因为它将被放置在浏览器异步事件队列的底部! - Morphyish
显示剩余3条评论
1个回答

1
await 关键字确保当前的方法线程在继续之前等待函数调用解析。
这就是为什么每个 flag set!after 日志之前打印的原因。
然而,您的执行被封装在一个 .map 中的异步箭头函数中。这意味着即使箭头函数本身被暂停,.map 也不会。如 @zerkms 所提到的,它将您的数组转换为一组 promises。
把它看作是对远程API的实际异步调用。 您不必等待服务器完成,.map 的所有调用都将完成,然后根据每个初始值返回进行评估。
这正是这里发生的事情。

1
我并不是说这个答案是错的,但其中有些部分看起来是误导性的,比如“然而你的执行被封装在箭头函数中”。作为箭头函数意味着增加了问题。 - zerkms
你有什么建议可以让它表达更准确,避免误导吗?英语不是我的母语,所以有时可能用词不当 :( - Morphyish
我使用@zerkms的解释重新表述了这段内容,希望能够澄清事情。 - Morphyish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接