异步
June 3, 2022
Reactor Pattern #
异步的底层原理 #
操作系统的 api
Callback #
Generator #
被调用时返回一个 generator 对象,函数体不会立即执行, 通过 generator 对象控制函数体的执行
function* f()
function *g()
function * h()
// 三种声明方式都可以, 第一种比较好
// as the star * denotes that it’s a generator function, it describes the kind, not the name, so it should stick with the function keyword
Promise1 #
Promise 对象表示异步操作的最终完成结果以及其值(或异常)
状态 #
-
pending
挂起,初始态
-
fulfilled
完成
-
rejected
失败
术语 #
-
settled
Promise fulfilled 或 rejected
-
thenable
在 Promise 规范化之前,有很多 Promise 的实现,所有的类 Promise 对象都实现了 Thenable 接口
Thenable 实现了 then 方法
Promise 也是 thenable
实例方法 #
挂在 Promise.prototype 上的方法,均 return promise, 可被链式调用
-
then() 返回新创建的 promise 对象,可被用来链式调用
第一个参数处理 fulfilled 态,第二个参数(可选)处理 rejected 态
-
catch()
没有处理 fulfilled 态 callback 的 then 方法
如过不需要立刻处理异常,可以在链式调用的最后放 catch
-
finally()
Promise settled 后调用,返回 promise
静态方法 #
-
Promise.reject()
-
Promise.resolve() 不仅 resolve promise,也 resolve thenable
处理异步任务并发的 4 个 api, 接收内容为 thenable 的 iterable 对象,返回新的 promise
-
Promise.all()
全 fulfill ,则 fulfill,任一 reject, 则 reject,并发执行
-
Promise.allSettled()
当所有的 promise fulfill 或 rejected 时 fulfill
-
Promise.any()
任一 fulfill,则 fulfill,全部 reject,则 reject
-
Promise.race()
任一 fulfill 则 fulfill,任一 reject 则 reject
异常处理 #
如果 promise 的 rejection 事件没有被任何 handler 处理,那么将会冒泡到调用栈的顶层,并触发两个事件
-
unhandledrejection
promise 被 reject,但是没有 reject handler
-
rejectionhandled
触发 unhandledrejection 事件的 promise rejection 被添加了 handler
Async/Await #
top level await 可以在 module 的顶层使用
Any modules that include this will wait for the fetch to resolve before running any code
Other microtasks can execute before the async function resumes
await literally suspends the function execution until the promise settles, and then resumes it with the promise result. That doesn’t cost any CPU resources, because the JavaScript engine can do other jobs in the meantime: execute other scripts, handle events, etc
Coroutine #
Observable #
IO #
网络请求,文件读写
同步io:应用初始化的时候读配置文件用,否则不建议使用同步 io,阻塞 eventloop,影响性能
优先级高于微任务
并发 #
并行 #
Worker Threads #
setImmediate #
非标准方法,不推荐使用,仅在 node 环境支持
宏任务,在 poll phase 阶段后执行
Event Loop #
等待任务,执行任务,继续等待任务(等待期基本不消耗 cpu 资源)
node 环境跟 browser 环境的 event loop 表现不一致
Browser event loop #
使用 libevent 来实现,单队列
伪代码表示
while (true) {
var nextTask;
// The browser notifies the Event Loop
// when it's time to render a frame
if (shouldRenderFrame()) {
nextTask = getRenderStepsTask();
} else {
// If it's not ready for a frame,
// the Event Loop should run the
// next Task.
nextTask = taskQueue.next();
}
// Run Task to completion
runOnMainThread(nextTask);
}
Node event loop2 #
多阶段,每阶段一个队列
使用 libuv 来实现
分不同的阶段 phase,每个阶段可以理解成一个队列
Node 11.0.0 修复了微任务的 bug
四个 phase (队列):
- expired timer callbacks
- I/O events
- immediate queues
- close handler
中间的俩任务队列:
-
process.nextTick3
任务队列,被 node 管理,仅在 node 环境支持
-
promise 微任务
Event Loop 最佳实践 #
事件的区别 #
浏览器事件:用户交互,脚本加载等,
服务端事件:文件 i/o,网络 i/o,
环境 | browser | node |
---|---|---|
— | — | — |
实现库 | libevent | libuv |
libuv #
Network I/O is not performed on the libuv thread pool
File I/O 在 libuv thread pool 里执行
dns.lookup() 在 libuv 线程池里执行
微任务 #
v8 术语,由引擎管理的任务队列
当前宏任务执行完后,引擎会清空微任务队列,再去执行下一个宏任务
通用 #
-
queueMicrotask
ecma-262 标准方法,用于注册微任务
-
.then/.catch/.finally
浏览器 #
- MutationObserver
Node #
- process.nextTick
宏任务 #
v8 术语,又称为 Task,‘macrotask’ 这个术语并不规范,仅口语上称之为 宏任务,在 html 规范里,并没有 macrotask 的定义4
WHATWG 并没有定义任务优先级应该怎么实现,只定义了不同的任务源
任务调度 API 草案5提供了定义任务优先级的能力, 有三个优先级,“user-blocking”, “user-visible” and “background”
不同的浏览器对宏任务的实现,没有完全一样的表现形式,例如:
- 在 chrome 里,每个 task queue 都有饥饿保护,防止 task queue 过度占用 eventloop ,从而让低优先级的队列有机会执行任务
- 在 chrome 里, setTimeout 仍然有最小的 1ms 延时
每个任务,都来自一个特定的任务源,每个任务源的 task 都有其对应的任务队列
每个 eventloop 都有一个或多个 task queue,每个 queue 里的任务都是按其入队顺序被处理
常用的 task source
- The DOM manipulation task source
This task source is used for features that react to DOM manipulations, such as things that happen in a non-blocking fashion when an element is inserted into the document.
-The user interaction task source
This task source is used for features that react to user interaction, for example keyboard or mouse input.
Events sent in response to user input (e.g. click events) must be fired using tasks queued with the user interaction task source. [UIEVENTS]
- The networking task source
This task source is used for features that trigger in response to network activity.
- The navigation and traversal task source
通用 #
- setTimeout
- setInterval
浏览器 #
事件监听函数,例如:脚本加载事件,页面交互事件
执行宏任务时,浏览器不会渲染
Node #
- setImmediate