JavaScript 异步回调 - Promise 和 setTimeout

Log*_*Wlv 9 javascript event-loop promise

在以下代码中:

setTimeout(() => console.log("hello"), 0);

Promise.resolve('Success!')
  .then(console.log)
Run Code Online (Sandbox Code Playgroud)

在我的理解中应该发生什么:

  1. setTimeout 被调用 =>print hello直接添加到回调队列,因为时间为 0
  2. Promise.resolve =>print Success!添加到回调队列

如果我没记错的话,回调队列是FIFO

但是代码输出是:

Success!
hello
Run Code Online (Sandbox Code Playgroud)

解释是什么?

zhu*_*ien 11

有 2 个单独的队列用于处理回调。一个队列和一个队列。setTimeout队列中的项目排入队列,同时承诺解析 - 到队列。当前正在执行的宏任务(在这种情况下是主脚本本身)是同步执行的,一行一行,直到它完成。完成后,循环执行微任务队列中排队的所有内容,然后继续执行队列中的下一项(在您的情况下是从 中console.log("hello")排队的setTimeout)。

基本上,流程如下所示:

  1. 脚本开始执行。

宏任务队列:[],微任务队列:[]。

  1. setTimeout(() => console.log("hello"), 0); 遇到导致在宏任务队列中推送新项目的情况。

宏任务队列:[ console.log("hello")],微任务队列:[]。

  1. Promise.resolve('Success!').then(console.log)已读。PromiseSuccess!立即解析为,并且console.log回调被排入微任务队列。

宏任务队列:[ console.log("hello")],微任务队列:[ console.log('Success!')]。

  1. 脚本完成执行,因此在继续宏队列中的下一个任务之前,它会检查微任务队列中是否有内容。
  2. console.log('Success!') 从微任务队列中拉出并执行。

宏任务队列:[ console.log("hello")],微任务队列:[]。

  1. 脚本再次检查微任务队列中是否还有其他内容。没有,所以它从宏任务队列中获取第一个可用任务并执行它,即 - console.log("hello")

宏任务队列:[],微任务队列:[]。

  1. 脚本执行完后console.log("hello"),它会再次检查微任务队列中是否有任何内容。它是空的,所以它检查宏任务队列。它也是空的,所以所有排队的东西都被执行并且脚本完成。

不过,这是一个简化的解释,因为它可能会变得更加棘手。微任务队列通常主要处理 promise 回调,但您可以自己将代码排入队列。微任务队列中新添加的项目仍然会在下一个宏任务项目之前执行。此外,微任务可以将其他微任务排入队列,这可能导致处理微任务的无限循环。

一些有用的参考资源:


You*_*saf 9

这里涉及两个不同的队列:任务队列任务队列

使用调度的回调函数setTimeout添加到任务队列中,而使用Promise调度的回调添加到微任务队列或作业队列中。

处理一个微任务队列:

  • 在每次回调之后,只要调用堆栈为空。
  • 每个任务之后。

另请注意,如果微任务队列中的微任务将另一个微任务排队,则该微任务也将处理任务队列中的任何内容之前进行处理。换句话说,处理任务队列中的下一个任务之前,微任务队列将被处理直到其为空。

以下代码片段显示了一个示例:

setTimeout(() => console.log('hello'), 0);

Promise.resolve('first microtask')
  .then(res => {
    console.log(res);
    return 'second microtask';
  })
  .then(console.log);
Run Code Online (Sandbox Code Playgroud)


在您的代码中,回调函数setTimeout被添加到任务队列中,并将Promise.resolve微任务排队到微任务队列中。该队列在脚本执行结束时进行处理。这就是为什么在“hello”之前记录“success”。

下图显示了代码的逐步执行:

在此处输入图片说明

进一步阅读的资源: