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

12 篇文章 0 订阅

从零开始

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

因此最终的输出结果是 1,7,6,8,2,4,9,11,3,10,5,12
在这里插入图片描述

async/await

当出现这样的代码( 头条面试题,好像很喜欢考这类的题目 )

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');

里面有async/await函数,async函数执行过程中,

按顺序执行代码,当遇到await,立即执行await后的函数,然后将await之后的代码加入微任务

结合我们上面同步-微任务-宏任务的代码执行顺序,因此整体输出为

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值