js是单线程机制,事件循环是惟一的,可是任务队列能够拥有多个html
任务队列分为两种:macroTask(宏任务)与microTask(微任务)。在最新标准中,它们被分别称为task与jobshtml5
macroTask大概包括:script(总体代码)、setTimeout、setInterval、 setImmediate、I/O、UI renderingnode
microTask大概包括: process.nextTick、Promise、 Object.observe(已废弃)、MutationObserver(html5新特性)promise
setTimeout/Promise等咱们称之为任务源。而进入任务队列的是他们指定的具体执行任务bash
来自不一样任务源的任务会进入到不一样的任务队列。其中setTimeout与setInterval是同源的函数
其中每个任务的执行,不管是macroTask仍是microTask,都是借助函数调用栈来完成oop
从script(总体代码)开始第一次循环,以后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),而后执行全部的microTask。当全部可执行的microTask执行完毕以后。循环再次从macroTask开始,找到其中一个任务队列执行完毕,而后再执行全部的microTask,这样一直循环下去。post
script(主程序代码)—>process.nextTick—>Promises…——>setTimeout——>setInterval——>setImmediate——> I/O——>UI renderingui
setTimeout(function(){console.log(1)},0);
console.log(2)
//2 1
复制代码
首先执行主线程中的同步任务,当主线程任务执行完以后,再从event loop中读取任务,所以先输出2,再输出1。spa
event loop读取任务的前后顺序,取决于任务队列(Job queue)中对于不一样任务读取规则的限定。
setTimeout(function () {
console.log(4);
}, 0);
Promise.resolve().then(function () {
console.log(2);
});
console.log(1);
//1 2 3
复制代码
先输出1,没有问题,由于是同步任务在主线程中优先执行,这里的问题是setTimeout和Promise.then任务的执行优先级是如何定义的?
任务队列分为:macroTask(宏任务)与microTask(微任务)。咱们来假设
macroTask队列包含任务: a1, a2 , a3
microTask队列包含任务: b1, b2 , b3
执行顺序为,首先执行marcoTask队列开头的任务,也就是 a1 任务,执行完毕后,在执行microTask队列里的全部任务,也就是依次执行b1, b2 , b3,执行完后清空microTask中的任务,接着执行marcoTask中的第二个任务,依次循环。
setTimeout(function(){
console.log(1)
},0);
new Promise(function(resolve,reject){
console.log(2);
resolve();
}).then(function(){
console.log(3)
}).then(function(){
console.log(4)
});
process.nextTick(function(){
console.log(5)
});
console.log(6);
//2 6 5 3 4 1
复制代码
script(主程序代码)——>process.nextTick——>promise——>setTimeout
更复杂的例子
new Promise(function(resolve,reject){
console.log(2);
setTimeout(function(){
resolve()
},0)
}).then(function(){
console.log(3)
}).then(function(){
console.log(4)
});
setTimeout(function(){
console.log(1)
},0);
process.nextTick(function(){
console.log(5);
});
console.log(6);
//2 6 5 1 3 4
复制代码
区别在于Promise的构造中,没有同步的resolve,所以promise.then在当前的执行队列中是不存在的,只有promise从pending转移到resolve,才会有then方法,而这个resolve是在一个setTimout时间中完成的,所以3,4最后输出
console.log('1');
setTimeout(function () {
console.log('2');
new Promise(function (resolve) {
console.log('3');
resolve();
}).then(function () {
console.log('4')
}).then(function () {
console.log('5')
});
})
new Promise(function (resolve) {
console.log('7');
resolve();
}).then(function () {
console.log('8')
})
setTimeout(function () {
console.log('9');
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log('10')
}).then(function () {
console.log('11')
});
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log('12')
})
})
复制代码
一、第一轮事件循环流程分析以下:
宏任务Event Queue | 微任务Event Queue |
---|---|
setTimeout1 | then1 |
setTimeout2 |
第一轮事件循环宏任务结束时各Event Queue的状况如上表,此时已经输出了1和7。清空微任务,执行then1,输出8。第一轮事件循环正式结束,这一轮的结果是输出1,7,8。
二、第二轮事件循环流分析:
从setTimeout1宏任务开始
宏任务Event Queue | 微任务Event Queue |
---|---|
setTimeout2 | then2 |
第二轮事件循环宏任务执行结束,执行微任务then2,输出4,then被分发到微任务Event Queue中,记为then3
宏任务Event Queue | 微任务Event Queue |
---|---|
setTimeout2 | then3 |
执行微任务then3,输出5。第二轮事件循环正式结束,这一轮的结果是输出2,3,4,5。
三、第三轮事件循环流分析:
从setTimeout2宏任务开始
宏任务Event Queue | 微任务Event Queue |
---|---|
then4 | |
then5 |
第二轮事件循环宏任务执行结束,执行微任务then4,输出10,then被分发到微任务Event Queue中,记为then6。执行微任务then5,输出12
宏任务Event Queue | 微任务Event Queue |
---|---|
then6 |
执行微任务then6,输出11。第三轮事件循环正式结束,这一轮的结果是输出9,10,12,11。
整段代码,共进行了三次事件循环,完整的输出为1,7,8,2,3,4,5,9,10,12,11。(node环境执行结果会有些不同)
参考文章:
从promise、process.nextTick、setTimeout出发,谈谈Event Loop中的Job queue