我们常说的浏览器Event Loop
EventLoop事件循环机制
1
事件循环机制
来看一段简单的代码:
console.log(1)
setTimeout(function() {
console.log(2)
}, 1000);
console.log(3)
// 打印顺序是 1 3 2
代码的运行顺序是从上到下1 2 3,但是打印的顺序却是1 3 2,这是为什么呢?
深入了解EventLoop
01
数据结构
堆(Heap):堆是线性数据结构,相当于一维数组,有唯一后继。
栈(Stack):栈是只能在某一端插入和删除的特殊线性表(后进先出)。
队列(Queue):特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表(先进先出)。
02
Event Loop
MacroTask(宏任务):script 全部代码、setTimeout、setInterval、setImmediate(Node 中,浏览器暂时不支持,只有 IE10 支持,具体可见 MDN)、I/O、UI Rendering。
MicroTask(微任务):Process.nextTick(Node 独有)、Promise、Object.observe(废弃)、MutationObserver(监听 Dom 结构变化)、PostMessage(window 之间进行通信的方法)。
JS 调用栈:JS 调用栈采用的是后进先出的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移出,直到栈内被清空。
同步任务和异步任务:Javascript 单线程任务被分为同步任务和异步任务,同步任务会在调用栈中按照顺序等待主线程依次执行,异步任务会在异步任务有了结果后,将注册的回调函数放入任务队列中等待主线程空闲的时候(调用栈被清空),被读取到栈内等待主线程的执行。
浏览器中的 Event Loop:Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。
异步的实现
可以从两个角度来看:
宏观:浏览器多线程
-
微观:Event Loop,事件循环
那么我们可以分为以下几个步骤:
首先我们会执行Javascript全局任务,也就是宏任务(MacroTask)。
当某个宏任务(MacroTask)任务执行完,执行MicroTask(微任务)内的任务。
MicroTask(微任务)执行完,再次从宏任务取下一个宏任务执行。
举个例子
例子1:
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
// 答案见底部
例子2:async/await
async function AwaitType1() {
console.log(1);
let res = await AwaitBackNum();
console.log(res);
}
async function AwaitBackNum() {
console.log(2);
return 3;
}
async function AwaitType2() {
console.log(4);
await AwaitBackPromise();
console.log(5);
}
async function AwaitBackPromise() {
return new Promise((resolve) => {
console.log(6)
resolve()
})
}
async function AwaitType3() {
console.log(7);
await AwaitBackError();
console.log(8);
}
async function AwaitBackError() {
return new Promise((resolve) => {
console.log(9)
})
}
AwaitType1();
console.log(11);
AwaitType2();
console.log(12);
AwaitType3();
console.log(10);
// 答案见底部
例子3:
async function test1() {
console.log(1);
let res = await test2();
console.log(res);
await test3();
console.log(11);
}
async function test2() {
console.log(4);
return 5;
}
async function test3(){
return new Promise(resolve=>{
console.log(15);
resolve();
})
}
async function test4() {
console.log(12);
await test5();
console.log(13);
}
async function test5() {
return new Promise(resolve=>{
console.log(14);
})
}
new Promise((resolve) => {
console.log(6);
setTimeout(() => {
console.log(8);
}, 200);
resolve()
}).then(() => {
console.log(10);
});
console.log(7);
test1();
setTimeout(() => {
console.log(1);
}, 200);
setTimeout(() => {
console.log(9);
test4();
}, 0);
// 答案见底部
试着做看看吧!
答案
例子1
Log: script start、script end、promise1、promise2、setTimeout
例子2
Log: 1、2、11、4、6、12、7、9、10、3、5
解析:
1.数字 3 为什么没有直接打印在 2 后面呢?
2.数字 8 为什么没打印?
例子3
Log: 6、7、1、4、10、5、15、11、9、12、14、8、1
喜欢记得加关注哦
长按关注
前端充电栈