EventLoop(事件循环)处理线程
关键词:Call Stack(调用栈),Task Queue(任务队列),线程(定时器触发线程,http异步线程)
let timeOutBack = function () {
console.log("timeOut回调");
}
let httpBack = function () {
console.log("http回调");
}
// 同步任务1
console.log("同步任务1");
// 异步定时任务1
setTimeout(timeOutBack, 1000);
// 异步http请求任务1
ajax.get('/info',httpBack)
// 同步任务2
console.log("同步任务2");
上述代码执行时,在内存中的处理流程分别是:
1.执行到同步任务1时,直接将同步任务丢到调用栈(Call Stack),执行完毕后丢弃。
2.执行到异步定时任务1时,不会立即执行调用栈,而是放到任务队列(Task Queue)里,但是不是立即放入任务队列,而是放到对应的定时器触发线程里进行托管。
- 执行到异步http请求任务1时,更异步定时任务1一样,不同的是放到对应的http异步线程里进行托管。
4.执行到同步任务2时,跟同步任务1一样,直接将同步任务丢到调用栈里,执行完毕后丢弃。
5.同步任务2执行完后,调用栈(Call Stack空了),事件循环(Event Loop)一直在轮询任务队列(Task Queue),但此时定时器触发线程依然在定时器触发线程中托管,任务队列依然是空的。1秒后,定时器触发线程里托管的异步定时任务1被放到任务队列里。大概一毫秒的时间,异步定时任务1被事件循环机制捕获到,丢入调用栈(Call Stack),执行完毕后丢弃。
6.第2秒后,http异步线程里托管的异步http请求任务1被放到任务队列里,大概一毫秒的时间,异步http请求任务1被事件循环机制捕获到,丢入调用栈(Call Stack),执行完毕后丢弃。
所以整个代码执行输出的结果为:同步任务1,同步任务2,timeOut回调,http回调。
1. 同步和异步机制
- 案例1
setTimeout(() => {
console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
console.log(3);
}, 10); // 如果将10设为0,那么实际为4ms,定时器最低4ms
console.log(4);
console.time("AA");
for (let i =0; i< 90000000; i++) { // AA 47ms左右
// do
}
console.timeEnd("AA")
console.log(5);
setTimeout(() => {
console.log(6);
}, 20);
console.log(7);
setTimeout(() => {
console.log(8);
}, 10);
输出结果为:2 4 AA 5 7 3 1 8 6
先同后异原则:
js会开辟一个主线程,所有的同步代码先执行,其它异步代码会放到对应的异步线程里进行托管,然后通过事件循环机制,将托管之后放到任务队列里的代码轮询后执行。
假设同步执行事件为0ms。
执行顺序如下,同步任务2,4执行完,47毫秒后执行同步任务5,7.同步代码结束。事件循环机制启动轮询,10ms放入宏任务-异步任务3,20ms放入宏任务-异步任务1(因为已经过了47ms,所以马上输出3,1)47+10ms后放入宏任务-异步任务8,47+20ms后放入宏任务-异步任务6.
所以最后输出结果: 2 4 AA 5 7 3 1 8 6
2. Promise
- 案例2
const button = document.getElementById("button")
button.addEventListener("click", () => {
Promise.resolve().then(() => console.log("Microtask 1"))
console.log("Listener 1");
})
button.addEventListener("click", () => {
Promise.resolve().then(() => console.log("Microtask 2"))
console.log("Listener 2");
})
页面点击 button结果为:Listener 1 Microtask 1 Listener 2 Microtask 2
js触发button.click()结果为:Listener 1 Listener 2 Microtask 1 Microtask
网友评论