在原文的基础上加了一点参考资料
题目的引出
event loop都不生疏,是指主线程从“使命行列”中轮回读取使命,比方
例1:
setTimeout(function(){console.log(1)},0);
console.log(2)
//输出2,1
在上述的例子中,我们邃晓起首实行主线程中的同步使命,当主线程使命实行终了后,再从event loop中读取使命,因而先输出2,再输出1。
event loop读取使命的先后递次,取决于使命行列(Job queue)中关于差别使命读取划定规矩的限制。比方下面一个例子:
例2:
setTimeout(function () {
console.log(3);
}, 0);
Promise.resolve().then(function () {
console.log(2);
});
console.log(1);
//输出为 1 2 3
先输出1,没有题目,由于是同步使命在主线程中优先实行,这里的题目是setTimeout和Promise.then使命的实行优先级是怎样定义的。
2 . Job queue中的实行递次
在Job queue中的行列分为两种范例:macro-task和microTask。我们举例来看实行递次的划定,我们设
macro-task行列包含使命: a1, a2 , a3
micro-task行列包含使命: b1, b2 , b3
实行递次为,起首实行marco-task行列开首的使命,也就是 a1 使命,实行终了后,在实行micro-task行列里的一切使命,也就是顺次实行b1, b2 , b3,实行完后清空micro-task中的使命,接着实行marco-task中的第二个使命,顺次轮回。
相识完了macro-task和micro-task两种行列的实行递次以后,我们接着来看,实在场景下这两种范例的行列里真正包含的使命(我们以node V8引擎为例),在node V8中,这两种范例的实在使命递次以下所示:
macro-task行列实在包含使命:
script(主程序代码),setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-task行列实在包含使命:
process.nextTick, Promises, Object.observe, MutationObserver
由此我们获得的实行递次应该为:
script(主程序代码)—>process.nextTick—>Promises…——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering
在ES6中macro-task行列又称为ScriptJobs,而micro-task又称PromiseJobs
3 . 实在环境中实行递次的举例
(1) setTimeout和promise
例3:
setTimeout(function () {
console.log(3);
}, 0);
Promise.resolve().then(function () {
console.log(2);
});
console.log(1);
我们先以第1小节的例子为例,这里遵照的递次为:
script(主程序代码)——>promise——>setTimeout
对应的输出顺次为:1 ——>2————>3
(2) process.nextTick和promise、setTimeout
例子4:
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
这个例子就比较庞杂了,这里要注意的一点在定义promise的时刻,promise组织部份是同步实行的,如许题目就水到渠成了。
起首剖析Job queue的实行递次:
script(主程序代码)——>process.nextTick——>promise——>setTimeout
I) 主体部份: 定义promise的组织部份是同步的,
因而先输出2 ,主体部份再输出6(同步状况下,就是严厉根据定义的先后递次)
II)process.nextTick: 输出5
III)promise: 这里的promise部份,严厉的说实际上是promise.then部份,输出的是3,4
IV) setTimeout : 末了输出1
综合的实行递次就是: 2——>6——>5——>3——>4——>1
(3)更庞杂的例子
setTimeout(function(){console.log(1)},0);
new Promise(function(resolve,reject){
console.log(2);
setTimeout(function(){resolve()},0)
}).then(function(){console.log(3)
}).then(function(){console.log(4)});
process.nextTick(function(){console.log(5)});
console.log(6);
//输出的是 2 6 5 1 3 4
这类状况跟我们(2)中的例子,区分在于promise的组织中,没有同步的resolve,因而promise.then在当前的实行行列中是不存在的,只要promise从pending转移到resolve,才会有then要领,而这个resolve是在一个setTimout时间中完成的,因而3,4末了输出。
知识点参考:
- process.nextTick(callback)
process.nextTick()要领将 callback 添加到”next tick 行列”。 一旦当前事宜轮询行列的使命悉数完成,在next tick行列中的一切callbacks会被顺次挪用。
这类体式格局不是setTimeout(fn, 0)的别号。它越发有效率。事宜轮询随后的ticks 挪用,会在任何I/O事宜(包含定时器)之前运转。
每次事宜轮询后,在分外的I/O实行前,next tick行列都邑优先实行。 递归挪用nextTick callbacks 会壅塞任何I/O操纵,就像一个while(true) 轮回一样。
function definitelyAsync(arg, cb) {
if (arg) {
process.nextTick(cb);
return;
}
fs.stat('file', cb); //触及io操纵
}
//这里process.nextTick就会壅塞io操纵