事件循环是js这门语言的一大特色。
了解事件循环机制,有助于平常开发中遇到的一些异步问题。
并且仍是前端面试一常常考点。
故本人结合一些文章和我的的一些开发经验,浅淡一下
一,js是一门单线程语言
js的单线程
a. js是一门单线程的语言。这意味着它在同一时间,只能作同一件事。
b. 但为了协调事件,用户交互,UI渲染和网络行为交互等。
c. 防止主线程被阻塞,Event Loop便应运而生。
如: 发送一个网络请求,须要等待必定时间,这个时间内主线程空闲出来作些其余事;
复制代码
为何js是单线程?
a. js主要是运行在浏览器的脚步语言,主要是操做dom;
b. 举个例子,若是js同时有多个线程。多个线程同时操做同一个dom,
这时浏览器该依据那个线程,如何判断优先级
c. 为了不上述问题,并下降复杂度,故js被设计成单线程语言。
复制代码
二,概念的理解
同步任务
同步任务指的是,在主线程上排队执行的任务,
只有前一个任务执行完毕,才能执行后一个任务;
复制代码
异步任务
异步任务指的是,不进入主线程、而进入"任务队列" (task queue)的任务,
只有"任务队列" 通知主线程,某个异步任务能够执行了,该任务才会进入主线程执行。
复制代码
异步执行机制
a. 全部同步任务都在主线程上执行,造成一个执行栈(execution context stack);
b. 主线程以外,还存在一个"任务队列" (task queue)。
只要异步任务有了运行结果,就在"任务队列" 之中放置一个事件。
c. 一旦"执行栈" 中的全部同步任务执行完毕,系统就会读取"任务队列" ,
看看里面有哪些事件。那些对应的异步任务,因而结束等待状态,
进入执行栈,开始执行。
d. 主线程不断重复上面的第三步。
复制代码
任务队列
"任务队列" 是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。
复制代码
事件循环
主线程从"任务队列" 中读取事件,这个过程是循环不断的,
因此整个的这种运行机制又称为Event Loop(事件循环)。
复制代码
宏任务与微任务
异步任务分为 宏任务(macrotask) 与 微任务 (microtask),
不一样的API注册的任务会依次进入自身对应的队列中,
而后等待 Event Loop 将它们依次压入执行栈中执行。
宏任务:script(总体代码)、setTimeout、setInterval、UI 渲染、
I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)
微任务:Promise 、 MutaionObserver、process.nextTick(Node.js环境)
复制代码
Event Loop(事件循环)
(1 )执行栈选择最早进入队列的宏任务(一般是script总体代码),若是有则执行;
(2 )检查是否存在 Microtask,若是存在则不停的执行,直至清空 microtask 队列;
(3 )更新render(每一次事件循环,浏览器均可能会去更新渲染);
(4 )重复以上步骤;
复制代码
宏任务 > 全部微任务(核心),上代码
<script>// 宏任务1
console.log('宏任务1'); // 宏任务1中的同步任务
setTimeout(() => {// 宏任务1中的另外一个宏任务3
console.log('宏任务1中的另外一个宏任务3');
new Promise((resolve, reject) => {
resolve('宏任务3中的微任务2');
}).then(data => {// 宏任务3中的微任务2
console.log(data)
})
}, 300);
new Promise((resolve, reject) => {
resolve('宏任务1中的微任务1');
}).then(data => {// 宏任务1中的微任务1
console.log(data);
setTimeout(() => {// 微任务1中的另外一个宏任务4
console.log('微任务1中的另外一个宏任务4');
}, 300);
});
</script>// 宏任务1
<script>// 宏任务2
console.log('宏任务2')
</script>// 宏任务2
复制代码
1. 宏任务1 =>宏任务1 中的微任务1
代表执行完宏任务就执行微任务(忽略宏任务2 ,便于理解)
2. 而后到 宏任务1 中宏任务3 =>宏任务3 中的微任务2
再次代表执行完本宏任务后就执行本宏任务下的微任务
3. 最后到微任务1 中的宏任务4
复制代码