从一道执行顺序的面试题,到最终搞懂这一块,记录下来,这是一些自己的见解,可能不是那么准确,但是确实能让我自己理解这一块内容了。
还有requestAnimation
一直跟宏、微分不开,所以也放到这一篇来科普
浏览器机制
- 用户界面:除了浏览器主窗口显示的请求界面外,都属于用户界面。
- 浏览器内核:在用户界面与渲染引擎之间传送指令。
- 渲染引擎:负责显示请求内容,解析 HTML 和 CSS,并将解析结果传送到界面上。
- 网络:用于网络调用,如 HTTP 请求。起接口与平台无关,为所有平台提供底层实现。
- 用户界面后端:用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。(个人理解是一些获取用户习惯的接口)
- JavaScript 解释器:用于解析和执行 JavaScript 代码。
- 数据存储:持久层。Cookie,session 等网络数据库,是一个完整且轻便的浏览器内数据库。
Nodejs 运行机制
- V8 引擎解析 JavaScript 脚本。
- 解析后代码调用 Node API。
- libuv 库负责 Node API 的执行。它将不同任务分配给不同的线程,形成一个 Event Loop(事件循环),以异步的方式将任务的执行结果返回给 V8 引擎
- V8 引擎再将结果返回给用户。
Nodejs 与任务队列相关的两个方法
process.nextTick
:process.nextTick()
的意思是定义一个动作,并让这个动作在下一次事件轮训的时间点上执行。
setImmediate
:一次Event Loop
执行完毕后调用。即当前轮询阶段完成后就执行函数。
宏任务与微任务
宏任务(macro-task):
- 包括整体代码 script
- setTimeout,setInterval->这个个人理解可以说是
异步宏任务
- requestAnimationFrame
- setImmediate(Node 端)
微任务(micro-task):
- Promise.then、catch、finally
- process.nextTick(Node 端)
- MutationObserver
执行顺序:
主流程宏任务-> 异步微任务-> 异步宏任务
async\await
其实async\await
本质上还是基于Promise
的封装,而Promise
是微任务的一种。所以在使用await
关键字与Promise.then
效果相似。
可以这样理解,在async
函数中在await
前都是同步执行的,await
前的代码都是new Promise
时传入的代码,然后执行await
这一行的代码,再将await
之后的所有代码都是在Promise.then
中的回调。
配合代码理解
1 | setTimeout((_) => console.log(4), 0); // 注册异步宏任务 |
- 首先执行主流宏任务,首先是 1,然后是 2。
- 主流宏任务执行完
- 执行微任务,打印出 3
- 执行异步宏任务,打印出 4
requestAnimation
requestAnimationFrame 是什么
requestAnimationFrame
方法告诉浏览器希望执行动画,并请求浏览器在下一次重绘之前调用回调函数来更新动画
回调函数
requestAnimationFrame
的回调函数会被传入DOMHighResTimeStamp
参数,DOMHighResTimeStamp
指示当前被requestAnimationFrame()
排序的回调函数被触发的时间。
语法
1 | window.requestAnimationFrame(callback); |
- callback
下一次重绘之前更新动画帧所调用的函数,该回调函数会被传入DOMHighResTimeStamp
参数,该参数与performance.now()
的返回值相同,它表示requestAnimationFrame()
开始去执行回调函数的时刻。
- 返回值
一个long
整数,请求 ID,是回调列表中唯一标识。是一个非零值,没别的意义。可以传这个值给window.cancelAnimationFrame()
以取消回调函数。
综合例子
1 | async function async1() { |
按照主流宏任务-微任务-异步宏任务的顺序 结果
1 | script start(主宏-1) |
这里为什么promise2
在async1 end
前执行,是因为,async1
函数 调用async2
函数,而async2
中也有异步调用,所以相当于
1 | function async1() { |