eventloop(事件循环机制)
在开始讲事件循环前,先对一些概念进行梳理:参考链接
浏览器常驻线程:
- 渲染引擎线程:顾名思义,该线程负责页面的渲染
- js引擎线程:负责js解析和执行
- 定时触发器线程:处理定时事件,比如
setTimeout
,setInterval
- 事件触发线程:处理DOM事件
- 异步http请求线程:处理http请求
js:单线程语言
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务,如果上一个任务响应时间长则会拖延整个程序的进行。
异步任务:不进入主线程,而进入任务队列中的任务,只有任务队列通知主线程,某个异步任务可以执行了,这个任务才会进入主线程执行。
任务队列(task queue):事件的队列,IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。
js中有两类任务队列:宏任务队列和微任务队列。宏任务队列可以有多个,微任务队列只有一个。
- 宏任务:script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)
- 微任务:Promise、MutaionObserver、process.nextTick(Node.js 环境)
代码执行优先级:
同步代码(宏任务) > process.nextTick > Promise(微任务)> setTimeout(fn)、setInterval(fn)(宏任务)> setImmediate(宏任务)> setTimeout(fn, time)、setInterval(fn, time),其中time>0
有一道题如下~
setTimeout(()=>console.log('a'),0)
var p = new Promise((resolve) => {
console.log('b')
resolve()
})
p.then(()=>console.log('c'))
p.then(()=>console.log('d'))
console.log('e')
请输出顺序
正确输出顺序为【becda】(选中查看)
解析:
settimeout加入宏任务队列
promise实例化部分为同步任务,直接执行,输出b
promise.then为异步任务,且是微任务,加入微任务队列*2
console为同步任务,直接执行,输出e
此时程序中主线程任务已全部执行完,开始读取任务队列中的任务
优先读取微任务队列,输出c,d
然后读取宏任务队列,输出a
总结:
同步和异步任务分别进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入任务队列。主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。 上述过程的不断重复就是我们说的 Event Loop (事件循环)。
async function func1() {
console.log(1)
await func2()
console.log(2)
}
async function func2() {
console.log(3)
}
console.log(4)
func1()
setTimeout(()=>{
console.log(5)
},0)
new Promise((resolve,reject)=>{
console.log(6)
resolve()
}).then((res)=>{
console.log(7)
})
console.log(8)
// 4,1,3,6,8,2,7,5