JavaScript 是一门单线程的语言,他的异步和多线程的实现是通过 Event Loop 事件循环机制来实现的。
大体有三个部分组成:
- 调用栈(call stack)
- 消息队列(Message Queue)
- 微任务队列(Microtask Queue)
当 Event Loop
开始的时候,会从全局代码,一行一行执行,遇到函数调用,会把它压入调用栈
中,被压入的函数叫做帧(Frame)
,但函数返回后,会从调用栈中弹出。
例如:
function fn1() {
console.log(1) //4.遇到 console.log(1) 把它压入栈中并执行,打印 1 ,再弹出
}
function fn2() {
console.log(2) //2.遇到 console.log(2) 把它压入栈中并执行,打印 2 ,再弹出
fn1() //3.fn1函数被调用,压入调用栈,并执行其中代码
console.log(3) //5.遇到 console.log(3) 把它压入栈中并执行,打印 3 ,再弹出
} //6.fn2函数调用完成,整个调用栈被清空
fn2() //1.将fn2函数压入调用栈,并执行其中代码
JavaScript中的异步操作,比如 fetch
事件回调 定时器 中的回调函数 会进入到 消息队列 中 成为消息
function fn1() {
console.log(1) //4.遇到 console.log(1) 把它压入栈中并执行,打印 1 ,再弹出
}
function fn2() {
setTimeout(()=>{ //2. 当setTimeout被压入栈时,它其中的回调函数,会进入到消息队列中,消息会在调用栈清空时执行
console.log(2) //7.执行消息
},100)
fn1() //3.fn1函数被调用,压入调用栈,并执行其中代码
console.log(3) //5.遇到 console.log(3) 把它压入栈中并执行,打印 3 ,再弹出
} //6.fn2函数调用完成,整个调用栈被清空,将消息压入调用栈中
fn2() //1.将fn2函数压入调用栈,并执行其中代码
而使用 promise async await
创建的异步操作 会加入到 微任务队列,他会在调用栈被清空时 立即执行 ,并且处理期间新加入的微任务会一同执行
var p = new Promise(resolve => { //1.Promise构造函数首先被压入调用栈
console.log(4) //2.console.log(4) 被压入并打印4,再弹出
resolve(5) // 3. resolve(5)被压入并调用再弹出
})
function fn1() {
console.log(1) //7.遇到 console.log(1) 把它压入栈中并执行,打印 1 ,再弹出
}
function fn2() {
setTimeout(()=>{ //5. setTimeout被压入栈,回调被放入消息队列
console.log(2) //13.最后执行消息队列中的消息 打印2
},100)
fn1() //6.fn1函数被调用,压入调用栈,并执行其中代码
console.log(3) //8.遇到 console.log(3) 把它压入栈中并执行,打印 3 ,再弹出
p.then(resolved => { //9.两个then中的回调会被放入微任务队列
console.log(resolved) //11.执行微任务打印5
})
.then(()=>{
console.log(6) //12.执行微任务打印6
})
} // 10.此时调用栈清空了,所以执行微任务队列中任务
fn2() //4.将fn2函数压入调用栈,并执行其中代码
主线程上的任务完成之后,就会从任务队列中取出任务来执行,此过程不断重复从而形成一个循环,称为eventLoop。
感谢阅读!!