js-Event Loop

什么是Event Loop?

js是单线程语言,代码需要一句一句往下执行,会出现阻塞。Event Loop是解决这一问题的机制,让js实现异步,在不同的js执行环境中(浏览器、node)表现不同。

浏览器中的Event Loop

js自上而下执行代码
同步任务,在调用栈中按照顺序等待主线程依次执行
异步任务,在EventTable中注册回调事件,等异步任务有结果后,将注册的回调函数放入对应的任务队列中

任务队列有宏任务队列和微任务队列。

一个线程仅有一个微任务队列,微任务包括:promise.then/catch/final、Mutation Observer、process.nextTick(node)。除此之外的事件,均为宏任务。

一个线程可以有多个宏任务队列分别处理不同类型的事件,如鼠标键盘事件可放在一个宏任务队列中,优先执行保证用户体验,其他类型事件可分别放在不同的宏任务队列中。

执行顺序:一个宏任务 -> 全部微任务 -> 一个宏任务 -> 全部微任务 -> ...直至两个队列都为空。同步任务相当于一个宏任务,在最开始执行。

  • promise.xxx属于es6范畴,规范中对于其是否视为微任务并不明晰,所以各浏览器实现上有所不同,影响执行顺序。Google将其视为微任务,Safari和Firefox将其视为宏任务。

  • async await是promise的语法糖,可转为promise.then来看待。

node中的Event Loop

node的Event Loop一共分为6个阶段,每个细节具体如下:

  • timers:本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。
  • pending callbacks:执行延迟到下一个循环迭代的 I/O 回调。
  • idle, prepare:仅系统内部使用。
  • poll:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞。
  • check:setImmediate() 回调函数在这里执行。
  • close callbacks:一些关闭的回调函数,如:socket.on('close', ...)。
   ┌───────────────────────────┐
┌─>│           timers          │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │
   └───────────────────────────┘

process.nextTick事件单独有一个队列存放,当每一阶段执行完毕后,先执行process.nextTick queue里的全部,再执行micro queue里的全部,然后再进入下一阶段。

为了和浏览器更加趋同,node v11版本将timer阶段的setTimeout,setInterval...和在check阶段的immediate改为一旦执行一个阶段里的一个任务就立刻执行微任务队列。node v10版本还是保持自己的执行方式。

对比总结

浏览器环境下,microtask 的任务队列是每个 macrotask 执行完之后执行。而在 Node.js 中,microtask 会在事件循环的各个阶段之间执行,也就是一个阶段执行完毕,就会去执行 microtask 队列的任务。

image.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容