javascript - "for await of"的控制流程是什么?

标签 javascript for-loop asynchronous async-await promise

(英语不是我的母语,所以如果你发现任何英语,请提前原谅我)
在这一点上,我对 Promises 和 Async await 函数非常满意,我也知道如何使用 Promise.all (它只是等待里面的所有 Promise 先解决,然后从所有 Promise 中提取值,然后返回一个数组使用带有这些值的 .then 函数。)
现在我想知道如何等待引擎盖下的工作。
我刚刚写了这段代码:

async function f() {

  let p1 = new Promise(function (r) {
    setTimeout(r, 3000, 1)
  })

  let p2 = new Promise(function (r) {
    setTimeout(r, 2000, 2)
  })

  let p3 = new Promise(function (r) {
    setTimeout(r, 1000, 3)
  })

  let arrayOfPromises = [p1, p2, p3];

  for await (let p of arrayOfPromises) {
    let data = await p;
    console.log(data)
  }
}

f();

现在我的问题是当它到达第一次迭代时会发生什么,它将到达一个 await 关键字,并且 await 立即返回一个待处理的 promise ,那么下面的代码是否在技术上评估为每次迭代中的未决 promise ?
  {
    let data = await p;
    console.log(data)
  }
所以我很困惑到底发生了什么,对于第一次迭代,setTimeout 将注册 3 秒,第二次注册 2,第一次注册 1。由于我们没有同步代码,所有的回调将一个一个运行,p3将首先被解析,然后是p2,最后是p1!
现在直观地说,我认为一旦 p1、p2、p3 被解析,这段代码“console.log(data)”将被放入微任务队列,因为我们的 p3 先被解析,我们应该得到 3,2,1 但我们'重新获得 1、2、3,那么我的理解中缺少什么?
(显然代码没有放入微任务队列,它的功能可能会像生成器函数一样执行类似 .next() 的操作,但我认为这在这里无关紧要)
似乎对于等待,第一个 promise 将首先被记录,无论它与迭代中的其他 promise 相比解决得有多快或多晚,那么到底发生了什么?

最佳答案

这是一个可以按您预期工作的代码:

async function printWhenResolved(p) { 
  const data = await p; console.log(data)
}

function createIteratorsAndBindPrinting() {   

  let p1 = new Promise(function (r) {
    setTimeout(r, 3000, 1)
  })

  let p2 = new Promise(function (r) {
    setTimeout(r, 2000, 2)
  })

  let p3 = new Promise(function (r) {
    setTimeout(r, 1000, 3)
  })

  let arrayOfPromises = [p1, p2, p3];

  for (let p of arrayOfPromises) {
    printWhenResolved(p)
  } 
  console.log('end of createIteratorsAndBindPrinting')
}
createIteratorsAndBindPrinting()
现在,从语义上讲,我认为处理看起来很清楚。在“for await (... of ...)”上,迭代一个可迭代对象,在每个点根据迭代的当前值是同步还是异步提供其值来做出决定。如果它同步提供它的值,我们立即执行“for”中的代码块。如果它异步提供它的值,我们等到提供了值,然后调用“for”中的 block 。
所以我们依次等待每个可迭代 - 然后才离开 for -堵塞。
在我提供的代码中,我们改为在每个 Promise 上调用一个异步函数,当它解析后,会将值记录到控制台。这也是为什么您的let data = await p是多余的(因为您的 await 也在 for -construct 中)。
这样,async函数是一种组织响应式代码的方法(即调用过程的代码以及例如 promise 的解析),而无需回调 hell 或 then-chaining。这可以说允许更多的模块化、表达性和可测试性。另见 https://stackoverflow.com/a/54497100/16005185
要查看与您的代码具体做什么的区别(没有冗余),请将上面的代码与以下代码进行比较:
async function createIteratorsAwaitAndPrint() {   

  let p1 = new Promise(function (r) {
    setTimeout(r, 3000, 1)
  })

  let p2 = new Promise(function (r) {
    setTimeout(r, 2000, 2)
  })

  let p3 = new Promise(function (r) {
    setTimeout(r, 1000, 3)
  })

  let arrayOfPromises = [p1, p2, p3];

  for await (let p of arrayOfPromises) {
    console.log(p)
  } 
  console.log('end of createIteratorsAwaitAndPrint')
}
createIteratorsAwaitAndPrint()
在这里我们做await在 for 构造中。注意“end of [...]”日志在我们完成迭代之前不会发生。

关于javascript - "for await of"的控制流程是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67961004/

相关文章:

javascript - 是否有 jQuery 选择器/方法来查找特定的父元素 n 级?

javascript - 如何删除 iFrame 中的某个元素

C# - while 循环内的 foreach 循环 - 中断 foreach 并立即继续 while 循环?

c - float 取意外高的值

python - Pandas 使用 for 循环连接数据帧

.net - 如何从线程中捕获异常?

javascript - 无需 Ajax 即可捕获表单提交事件

javascript - LG Web OS 无法打开内部链接

javascript - 确保两个函数依次执行,其中第一个函数内部有异步调用

asynchronous - Singleton EJB 异步方法 (LockType.Write) 阻塞直到方法处理,或者它会在将控制权返回给客户端后立即释放锁?