可以先了解一下这个demo,猜一下他的输入顺序
new Promise( resolve=>{
resolve(1);
Promise.resolve().then(()=>{console.log(2)});
console.log(4);
}).then(t=>{
console.log(t)
});
console.log(3)
什么是EventLoop ?
为了协调事件、用户交互、脚本、UI渲染和网络请求等行为,防止线程阻塞,EventLoop的方案就应运而生。
EventLoop包含两类:
1、基于Browsing Context
2、基于Worker
两者是独立的,也就是说每个javascript运行的“线程环境”都有一个独立的EventLoop,每个WebWorker也有一个独立的EventLoop
任务队列
事件循环是通过任务队列的机制进行协调的,一个EventLoop可以有一个或者多个任务队列,一个任务队列便是一个有序的任务队列的集合,每个任务都有一个任务源,源自同一个任务源的task必须放在同一个任务队列,不同源的则被添加到不同的队列。在事件循环中,每进行一次循环操作成为tick,每一次tick的任务处理模型如下:
1、把oldestTask标记为oldest Task,UA可以选择任意任务队列,如果没有选择跳到Microtasks微任务处理
2、将当前运行的任务设置为oldestTask
3、运行oldestTask
4、将事件循环的当前运行任务,置为null
5、从任务队列中移除oldestTask
6、检查是否存在microTasks,如果存在则不停的执行,直至清空microTasks queue
7、更新render
(判断Document在此时间点渲染是否会“获益”,浏览器只需要保证60Hz的刷新频率即可,若>EventLoop频率过高,即使渲染了浏览器也无法及时展示,所以并不是每一轮EventLoop都会执行>UI render
1、执行渲染所需要的工作,如触发resize、scroll、建立媒体查询、运行css动画等
2、执行Animation Frame Callbacks
3、执行IntersectionObserver callback
4、渲染UI)
8、主线程重复执行上述步骤
异步任务可以分为macrotask和microtask,不同的API注册的异步任务会依次进入自身相应的队列中,然后等待EventLoop将它们依次压入执行栈中执行。
macroTask 又称之为task
microTask 又称之为job
macroTask包含:script整体代码、setInterval、setTimeout、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(nodejs环境)
microTask包含:Promise.then、MutationObserver、process.nextTick(nodejs环境)
在node环境当中会优先清空nextTick queue,再清空other queue,常见的如Promise
此外timers(setTimeout、setInterval)会优先与setImmediate执行,因为前者在timer阶段执行,后者在check阶段执行。
Nodejs大致的事件虚汗执行顺序如下