持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第31天,点击查看活动详情
1. 引言
今天我们来讨论下JavaScript 中的一个经典问题:什么是事件循环(Event Loop)? 首先,大家应该都知道JavaScript是浏览器运行的脚本语言,它的主要用途就是与服务端数据通信,操作客户端业务逻辑从而操作dom。
2. 介绍
因为JavaScript的单线程特性,当JavaScript 同时两个线程同时操作一个dom时候该优先进行那个?这时候就会涉及到主线程,执行栈和任务队列。
- 什么是任务队列? 是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在“任务队列”中添加一个事件,表示相关的异步任务可以进入“执行栈”,主线程读取“任务队列”,即使读取里边有哪些事件。”
任务队列分为同步任务和异步任务。
-
同步任务
-
异步任务 异步任务分为:
宏任务(macrotask):script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)
微任务(microtask):Promise、 MutaionObserver、process.nextTick(Node.js环境) -
什么是执行栈?
遵循先进先出原则,一种结构,放的是函数的执行环境,每一次函数执行之前,他的所有内容全部会放到执行栈中,函数调用之前,会创建执行环境,放到执行栈当中,函数调用完成,销毁执行环境。 -
什么是主线程?
简单说,浏览器的两个线程:一个负责程序本身的运行,称为”主线程”;另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为”Event Loop线程”(可以译为”消息线程”)。
3. 任务流程
当JavaScript 在处理任务的时候,主线程规定现在执行执行栈中的哪个事件,从任务队列中执行某个任务,并采取主线程循环:即主线程会不停的从执行栈中读取事件,会执行完所有栈中的同步代码。当同步任务都执行完毕之后,才会到任务队列里面执行异步任务。
在异步任务队列中,也分为宏任务和微任务分别在宏任务队列和微任务队列,只有微任务队列中的任务全部执行完毕后,才会执行宏任务队列里面的任务。如此反复循环直到任务队列中清空为止。以上宏任务与微任务反复循环即为是事件循环。
3. 事件循环特点
- 执行栈选择最先进入队列的宏任务(通常是script整体代码),如果有则执行
- 检查是否存在 Microtask,如果存在则不停的执行,直至清空 microtask 队列
- 更新render(每一次事件循环,浏览器都可能会去更新渲染)
- 重复以上步骤
图示:
4. 总结
浏览器在执行代码的时候碰到同步任务会一步一步往下执行,碰到异步任务会放入到任务队列中等同步任务执行完以后再放入到主进程中执行;所以每一轮事件循环的执行顺序应该都是同步优先异步执行,异步又分为宏任务和微任务,当执行异步任务的时候微任务优先于宏任务,本轮的循环执行结束后才会去上一层进行往下执行;
5. 思考题
async function async1(){
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
requestAnimationFrame(function(){
console.log('requestAnimationFrame')
})
async1();
new Promise(function(resolve){
console.log('promise1')
resolve();
}).then(function(){
console.log('promise2')
})
答案是什么呢?请留下你的答案吧