【问题标题】:Async code execution explanation异步代码执行说明
【发布时间】:2018-01-25 17:30:48
【问题描述】:

我一直在努力解决我遇到的问题,但没有成功,因此我请求您的帮助。

让我们考虑以下示例:

const async = require('async')
var counter = 0

var test = cb => {
  if (++counter < 3) {
    setTimeout( () => {
      async.each([0,1,2], (item, callback) => {
        console.log(item)
        console.log('counter inner ' + counter)
        test(cb)
        callback()
      }, () => {
          cb()
       })
    })
  } else {
      console.log('counter ' + counter)
  } 
}

var prom = new Promise( (res, rej) => test( () => res('done') ) )
prom.then(res => console.log(res))

输出是:

0
counter inner 1
1
counter inner 2
counter 3
2
counter inner 3
counter 4
done
0
counter inner 4
counter 5 
1 
counter inner 5
counter 6
2
counter inner 6
counter 7

我不明白的是它是如何在第二个0 之后打印的counter inner 4。不应该打印counter inner 1?一个承诺只被解决一次?第二个解决调用会发生什么?谢谢。

【问题讨论】:

标签: javascript node.js asynchronous es6-promise


【解决方案1】:

逐步解释有点困难,但我会向后尝试 - 这意味着从您的输出中我会尝试提及您的代码中的哪一行以及何时输出。

// obviously starting inside the callback async runs for each of your 3 items

    0 // console.log(item);
    counter inner 1 // console.log('counter inner ' + counter);

    1 // console.log(item);
    counter inner 2 // console.log('counter inner ' + counter);
    counter 3 // let's not forget you also call callback() for each item 
             // which evaluates this condition if (++counter < 3) and starting from here,
             // it will be true and will run your else statement
             // console.log('counter ' + counter);

    2 // console.log(item);
    counter inner 3 // console.log('counter inner ' + counter);
    counter 4 // console.log('counter ' + counter);

// at this point your promise is already resolved (and yes, only once)
    done // prom.then(res => console.log(res))

// Now you are probably questioning where the following output comes from
// remember this condition? if (++counter < 3)
// before starting to evaluate to false and printing to console, once it
// evaluated to true so setTimeout was called with your async callback
// which scheduled it to run on the next "tick"*, which is right after the
// promised was resolved

    0
    counter inner 4
    counter 5 
    1 
    counter inner 5
    counter 6
    2
    counter inner 6
    counter 7

这是setTimeout() 的超时时间为 0 的效果。这就像 C 中的线程/进程 yield。虽然它似乎说“立即运行”,但它实际上是在执行队列的末尾重新排队新的 JavaScript 代码。

根据setTimeout文档,它的第二个参数如下:

时间,以毫秒(千分之一秒)为单位,计时器应该 在执行指定的函数或代码之前等待。如果这 参数省略,取值为0,表示执行 “立即”,或者更准确地说,尽快。请注意,在 无论哪种情况,实际延迟都可能比预期的要长;

您可以在此处阅读有关reasons for delays longer than specified 的更多信息。

您可以通过将if 条件更改为if(++counter &lt; 2) 来轻松验证我刚刚解释的内容。由于它永远不会评估为 true,因此您会看到控制台输出停止在 "done"

【讨论】: