EventLoop
参考链接:
https://juejin.im/post/5c2ec3b66fb9a049eb3c1012#heading-17
https://juejin.im/post/5b07d0d1f265da0de02f40e3
JS 线程
我们都知道JS 是单线程的,即在同一时间只能执行一个js任务,Why?? 因为js的主要作用是与用户互动和操作DOM,试想若对DOM 的操作分发到两个并行互不线程里面,一个在DOM 上添加内容,一个在DOM 上删除内容,那么会出现什么情况??
浏览器
我们知道了 js 是单线程的,那么诸如onclick回调,setTimeout,Ajax这些都是怎么实现的呢?是因为浏览器或node(宿主环境)是多线程的,即浏览器搞了几个其他线程去辅助JS线程的运行,浏览器很多线程,如:
- GUI 渲染线程
- js 引擎线程(主线程:即运行JS代码的那个线程(不包括异步的那些代码))
- 定时器触发线程(setTimeout 等)
- 浏览器事件线程(onclick)
- http 线程
- EventLoop轮询处理线程
......
其中,1、2、4为常驻线程, 1和2线程是互斥的
来个例子:
1 var a = 2;
2 setTimeout(fun A) // 假设时间设置 2 秒
3 ajax(fun B) // 假设请求时间设置 3秒
4 console.log()
5 dom.onclick(func C) // 假设用户在4秒是点击
在执行主线程的时候,碰到2 setTimeout(fun A)时会启动定时器触发线程去执行这段代码
在执行3 ajax(fun B)时或启动http线程执行这段代码
在执行5 dom.onclick(func C)时启动浏览器事件线程去执行这段代码
在2秒后 setTimeout(fun A) 执行完毕,通知EventLoop轮询处理线程过来取回调函数 fun A,放到任务消息队列里面(此时若主线程访问消息任务队列则执行fun A)
在3秒后 ajax(fun B) 执行完毕,通知EventLoop轮询处理线程过来取回调函数fun B,放到任务消息队列里面(此时若主线程访问消息任务队列则执行fun B)
在4秒后 dom.onclick(func C) 执行完毕,通知EventLoop轮询处理线程过来取回调函数 fun C,放到任务消息队列里面(此时若主线程访问消息任务队列则执行fun C)
注意
- 主线程把setTimeout、ajax、dom.onclick分别给三个线程,他们之间有些不同
- 对于setTimeout代码,定时器触发线程在接收到代码时就开始计时,时间到了将回调函数扔进队列。
- 对于ajax代码,http 异步线程立即发起http请求,请求成功后将回调函数扔进队列。
对于dom.onclick,浏览器事件线程会先监听dom,直到dom被点击了,才将回调函数扔进队列。
eventLoop(事件循环机制)
若将上面的任务消息队列在细化为两种:宏任务和微任务
宏任务
· setTimeout,setInterval,setImmediate(ie下生效),MessageChannel(消息通道)
· UI 交互事件
· 网络请求等等
微任务
· Promise.then()
· process.nextTick(Node.js 环境)
注:new Promise本身属于同步代码,只有 .then() 才是微任务
时间循环流程
执行同步代码 => 执行微任务 => 执行宏任务的第1项(若遇到微任务,将微任务放到队列中)=> 执行微任务 => 执行宏任务的第2项 => 执行微任务......
说了这么多废话,还是得练题 >_> ....
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
// 1 7 6 8 2 4 9 11 3 10 5 12
自己做的输出结果 1 7 6 8 2 4 9 11 3 5 10 12 为啥最后四个数字和运行出来的不一样呢??因为微任务中 nextTIck 会比 .then 先执行。
再来一道,争取把自己绕晕:
console.log(1);
setTimeout(() => {
console.log(2);
Promise.resolve().then(data => {
console.log(3);
});
});
new Promise((resolve) => {
resolve()
console.log(4)
}).then(() => {
console.log(5);
setTimeout(() => {
console.log(6);
});
}).then(() => console.log(7))
console.log(8);
// 1 4 8 5 7 2 6 3