js的执行顺序:从头到尾,一行一行执行代码,这是前提。
因此这样的代码的代码完全能够按照我们的预想输出
console.log(1);
console.log(32)
只到遇到了下面的代码
console.log(1)
setTimeout(()=>{
console.log(2)
},0)
console.log(3)
js是一门单线程语言,因此如果在js的线程中出现耗时操作,就容易堵塞后续代码的执行。因此在js中如果碰到一些可能需要耗费一些时间的操作,像setTimeout,ajax的回调函数(称其为异步操作)等,js会将其放入一个代办任务队列taskqueue中,当js按顺序执行完其他同步的,不耗时的操作之后,会去依次执行taskqueue队列中的任务。
因此下面代码,结果在意料之中:
console.log(1)
setTimeout(()=>{
console.log(2)
},0)
console.log(3)
输出1 3 2
另外一种情况
如果代码是这样的
console.log(1)
setTimeout(()=>{
console.log(2)
},200)
console.log(3)
setTimeout(()=>{
console.log(4)
},100)
console.log(5)
输出13542
js执行完同步代码之后,会判断耗费的时间是否已经达到setTimeout设定的时间,如果达到对应的时间,就执行相应的setTimeout内部的代码;如果没有达到时间,就进入下一次的eventLoop。js内部会不断去查找是否有可以执行的程序。
这样能够解释setTimeout和setInterval的调用顺序问题,但是如果碰到存在回调函数的情况:
setTimeout(()=>{
console.log(1)
},0)
new Promise((resolve)=>{
console.log(2);
resolve();
}).then(()=>{
console.log(3)
})
console.log(4)
刚碰到这道题时,我想着回调函数应该也和setTimeout函数放入同一个队列,那样的话setTimeout应该执行顺序在then回调的前面,应该 输出 2413,但实际上输出了2431。之后才发现,js的执行中存在 宏任务和微任务。
宏任务:普通的script代码(同步代码),setTimeout,setInterval;
微任务:process.nextTick,promise.then()
而js在执行完同步代码之后,会首先去执行微任务队列中待执行的代码,然后再去执行宏任务中的代码
setTimeout的执行机制实际是:当代码执行时间等于设定的时间时,会自动将setTimeout内部的代码加入宏任务的执行队列中,setInterval就是定期将任务放入宏任务队列中;
而回调函数和node中的process.nextTick会进入微任务队列中,在事件循环中先被js执行。
通过代码来理解一下问题
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')
})
},0)
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')
})
},0)
首先第一次事件循环,根据先执行微任务的原则,先除去两个setTimeout
console.log('1');
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
很明显 应该输出1768
然后第二次事件循环,执行setTimeout中的内容,先只留下setTimeout
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
},0);
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
},0)
由于时间间隔都为0,可以认为执行完第一次事件循环之后,两个setTimeout中的任务都进入了宏任务队列,代码可以改为:
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
同步流程2,4,9,11
微任务中存在nextTick和回调函数,在这里需要注意,nextTick的执行顺序>回调函数
因此在执行微任务时,执行的顺序是3,10,5,12