#7Event loop:宏任务与微任务
事件循环
在计算机领域中事件循环(event loop),又称为消息分发器(message dispatcher)、消息循环(message loop)、消息泵(message pump)或运行循环(run loop),是一种程序构造或设计模式,负责等待并分发程序中的事件或消息。它的工作方式是向内部或者外部的“事件提供方”发出请求(请求通常会被阻塞,直到有新事件产生),待请求被处理后调用所获得的事件对应的回调函数(即“分发事件”)。
事件循环可以与反应器(reactor)结合使用,只要事件提供方采用可以被 poll(指类似 select、epoll 的系统调用,不是指轮询)的文件接口。事件循环几乎总是与消息发起方异步地执行。
当一个事件循环构成程序的中心控制流时(通常是这样),可以称之为主循环或者主事件循环。这些名称是合适的,因为这样的事件循环位于程序控制流的最顶层。
事件循环是实现进程间通信的方法之一。
JS 中的事件循环
事件循环的概念非常简单。有一个无限循环,JavaScript 引擎等待任务,执行它们然后休眠,等待更多任务。
- 宏任务: setTimeout、setInterval、setImmediate、requestAnimationFrame、I/O、UI 渲染
- 微任务: process.nextTick、Promises、queueMicrotask、MutationObserver
Node.js
什么是事件循环 (What is the Event Loop)?
事件循环是 Node.js 处理非阻塞 I/O 操作的机制——尽管 JavaScript 是单线程处理的——当有可能的时候,它们会把操作转移到系统内核中去。
目前大多数内核都是多线程的,它们可在后台处理多种操作。当其中的一个操作完成的时候,内核通知 Node.js 将适合的回调函数添加到轮询
队列中等待时机执行。
事件循环机制解析 (Event Loop Explained)
当 Node.js 启动后,它会初始化事件循环,处理已提供的输入脚本(或丢入到 REPL),它可能会调用一些异步的 API、调度定时器,或者调用 process.nextTick(),然后开始处理事件循环。
# 事件循环操作顺序的简化概览
# 1. 每个框为事件循环机制的一个阶段
# 2. 每个阶段都有一个 FIFO 队列来执行回调
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
timers
: 定时器 - 本阶段执行已经被setTimeout()
和setInterval()
的调度回调函数。pending callbacks
: 待定回调 - 执行延迟到下一个循环迭代的 I/O 回调。idle, prepare
: 仅系统内部使用。poll
: 轮询 - 检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和setImmediate()
调度的之外),其余情况 node 将在适当的时候在此阻塞。此阶段有两个重要功能:- 计算应该阻塞和轮询 I/O 的时间。
- 然后,处理
轮询
队列里的事件。
check
: 检测 -setImmediate()
回调函数在这里执行。close callbacks
: 关闭的回调函数 - 一些关闭的回调函数,如:socket.on('close', ...)
。
在每次运行的事件循环之间,Node.js 检查它是否在等待任何异步 I/O 或计时器,如果没有的话,则完全关闭。