同步任务
因为 JavaScript 是单线程的,所以同步任务只能在主线程上排队执行,并且只有当前一个任务完成才能执行下一个任务
当 JavaScript 运行时,每个函数的执行都会进入调用栈(Call Stack)中,当前函数执行完毕后将其移出栈,然后执行下一个函数,如果其中有非常耗时的函数执行,那么调用栈就阻塞了,后面的任务只能一直处于等待状态无法执行,且此时 UI 对面任何用户操作都不会响应,看起来就像程序崩溃了一样。
为了解决这个问题,我们有了异步任务。
异步任务
常见的异步:
setTimeout()
setInterval()
Promise.resolve().then()
fetch.then()
异步任务在执行之前不会进入调用栈,它进入事件循环队列(Event Loop Queue),当主线程执行完毕后,且异步时机到了,就会将事件循环队列中的任务推入调用栈中执行。
Event Loop
Event Loop 的工作非常简单,它只是监控调用栈和回调队列(包含每个事件对应的回调函数,比如 click 的回调函数)。如果调用栈为空,Event Loop 将队列中的第一个任务移除并推送到调用栈。
从 ES6 开始,Event Loop 处理异步也分两种,在Macrotask
中处理setTimeout
这类的,在Microtask
中处理Promise
这类的。它会优先处理Microtask
中的任务
browser event model
Node.js 中的 Event Loop
Node.js 中的 Event Loop 分为 6 个阶段,依次是:
- timers 定时器
- 这个阶段执行已被
setTimeout()
和setInterval()
调度的回调函数
- 这个阶段执行已被
- pending callbacks 待定回调
- 执行延迟到下一个循环迭代的I/O回调
- idle, prepare
- Node.js 系统内部使用
- poll 轮询
- 检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和
setImmediate()
调度的之外),其余情况 node 将在适当的时候在此阻塞(等待)
- 检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和
- check 检测
setImmediate()
回调函数在这里执行
- close callback 关闭的回调函数
- 一些关闭的回调函数,如:
sockt.on('close', ...)
- 一些关闭的回调函数,如:
process.nextTick()
的回调会在每个阶段结束之前调用。
每次运行的事件循环之间,Node.js 检查它是否在等待任何异步 I/O 或计时器,如果没有的话,则完全关闭。
当我们在 Node 中首次执行 JavaScript 代码时,会有开启 Event Loop 和执行 JavaScript 两个阶段,这两个阶段是串行发生,开启速度受制于机器性能,这就是为什么在主线程中执行这两个异步,他们的顺序并不总是相同。
Promise Aplus
这里是我根据规范实现的 z-promise.js,另外还根据 promise aplus 画了两张流程图:
promise constructor
promise.then
以下内容待更新
-
async/await