理解js中的同步和异步执行

从零开始

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的执行顺序>回调函数
理解js中的同步和异步执行_第1张图片
因此在执行微任务时,执行的顺序是3,10,5,12

因此最终的输出结果是 1,7,6,8,2,4,9,11,3,10,5,12
理解js中的同步和异步执行_第2张图片

你可能感兴趣的:(前端,js)