1. 观察者模式

Js 内核通过观察者模式实现事件驱动(通过绑定事件,注册触发者及行为函数),从而使异步回调及非阻塞IO成为可能,我认为这是Js 语言设计核心。
观察者模式(有时又被称为模型(Model)-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。

2. 浏览器事件环

2.1 浏览器工作线程

  • GUI 渲染线程(解析html,css, 构建dom树,渲染,绘图)
  • JavaScript引擎线程(与 GUI渲染线程互斥)
  • 定时触发器线程
  • 事件触发线程(主要负责将准备好的事件交给 JS引擎线程执行)
  • 异步http请求线程

2.2 Micro-Task 与 Macro-Task

  • 常见的 macro-task 比如:setTimeout、setInterval、script(整体代码)、 I/O 操作、UI 渲染等。
  • 常见的 micro-task 比如: new Promise().then(回调)、MutationObserver(html5新特性) 等。

    2.3 执行流程

    image.png
    每执行一个宏任务完成,会查看微任务列队,并执行清空,然后查看下一个宏任务。

3. Nodejs 事件环

3.1 Nodejs 工作线程

nodejs 代码运行在单线程上
V8 提供 js 解析,编译,优化,垃圾回收,是多线程。
libuv库 提供系统底层调用(跨平台的异步IO库)。多线程。其中,通过epoll,kqueue,event ports和IOCP来实现异步network IO。通过 线程池实现dns,file 等操作。

3.2 Micro-Task 与 Macro-Task

Node端事件循环中的异步队列也是这两种:macro(宏任务)队列和 micro(微任务)队列。

  • 常见的 macro-task 比如:setTimeout、setInterval、 setImmediate、script(整体代码)、 I/O 操作等。
  • 常见的 micro-task 比如: process.nextTick、new Promise().then(回调)等。

    3.3 运行流程

  • timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调

  • I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
  • idle, prepare 阶段:仅node内部使用
  • poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里
  • check 阶段:执行 setImmediate() 的回调
  • close callbacks 阶段:执行 socket 的 close 事件回调

image.png

3.4 process.nextTick

这个函数是独立于 Event Loop 之外的,它有一个自己的队列,当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行。

在Node.js中,microtask会在事件循环各个阶段之间执行,也就是一个阶段执行完毕,就会去执行microtask队列的任务。Node11版本后,宏任务与微任务 执行顺序与浏览器保持一致(宏任务->tick队列->微任务队列->下一个宏任务)。

参考文档
js 事件模型
观察者模式
宏任务-微任务-event-loop
浏览器event-loop 在线演示
浏览器与Node的事件循环(Event Loop)有何区别