浏览器中的事件循环
JavaScript 代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。整个执行过程,我们称为事件循环过程。一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。任务队列又分为 macro-task(宏任务)与 micro-task(微任务),在最新标准中,它们被分别称为 task 与 jobs。
macro-task 大概包括:
- script(整体代码)
- setTimeout
- setInterval
- setImmediate
- I/O
- UI render
micro-task 大概包括:
- process.nextTick
- Promise
- async/await(还是 Promise)
- MutationObserver(HTML5)
整体流程大概为:
执行 macro-task,然后执行该任务产生的 micro-task,若 micro-task 在执行过程中新生成了 micro-task,则继续执行 micro-task。直到无 micro-task,返回 macro-task,进行 event loop。
答案为:
- async2 end
- Promise
- async1 end
- promise1
- promise2
- setTimeout
async/await 执行顺序
我们知道 async 隐式返回 Promise 函数,可以理解为 await 后的语句执行结束后,await 产生一个 micro-task。但是时机在与执行完 await 后的语句之后,会暂停当前执行,转而执行其他 loop。然后按照 micro-task 的注册顺序,继续执行。最后进入下一个 macro-task。
Node 和 Browser 的 Event Loop 差异
- Browser 中的 micro-task 是在每个相应的 macro-task 中执行的。
- NodeJS 中的 micro-task 是在不同阶段中执行的。
NodeJS 版本差异
NodeJS11 之后,会在执行一个 macro-task 之后立即执行他的 micro-task 队列。
1 | setTimeout(() => { |
- node11 之前:timer1 -> timer2 -> promise1 -> promise2
- node11 之后:timer1 -> promise1 -> timer2 -> promise2