前言
Javascript的同步任务和异步任务也是一个面试的高频考点,这里讲一个笑话,话说当初刚出来实习的时候,记得去了一个贼偏僻的地方面试前端,转正4K,实习3k,包住不包吃。当时记得贼清楚的就是一个女面试官,看着比我都还小的样子,然后那个面试官就问了个关于同步异步的问题,具体是什么不记得了,大概就是:
console.log(1)
setTimeout(() => console.log(2),0)
console.log(3)
问打印顺序是什么,当时是真的不知道她到底要考什么,不知道什么叫同步任务和异步任务,一顿瞎猜,果断没猜对,然后就挂了。现在想想,这不就是同步异步问题吗?而且是最简单的那种。
单线程
众所周知,Javascript是一门单线程语言,同一个时间只能做一件事,如果在同个时间有多个任务的话,这些任务就需要进行排队,前一个任务执行完,才会执行下一个任务。
同步任务
简单来理解就是一段代码按照编写顺序,从上到下依次执行。
异步任务
异步任务又分为宏任务和微任务,其中宏任务主要有:script整体代码、setTimeout、setInterval、setImmediate、Ajax、DOM事件,微任务主要有:process.nextTick、MutationObserver、Promise.then catch finally。而浏览器先执行完同步任务后,再取出第一个宏任务,当第一宏任务执行完毕之后,会先取出微任务队列中的所有微任务执行,执行完毕之后再取出宏任务,依次循环,直到执行完所有任务。这里特别备注一下:Promise里面的代码属于同步任务,回调里面的才是微任务。
我记得之前在某篇文章中看到过一个解释就是:这就好比银行排队办理业务,这时候迎面走来一位大爷,当大爷任务办理完后。银行柜台说,大爷这边存5万送一桶5L的油,您想要这桶油吗?于是大爷高高兴兴的存了5w,拿了油走了。在这个故事中,大爷办理的业务就可以比作宏任务,而最后的存5w,送油就好比微任务,宏任务执行完了后就把微任务也取出来执行了,不然让大爷存5w再去排一个号的话,大爷怕是要让你知道什么才是你大爷才是你大爷。
这里贴一段代码:
// 1
console.log('1');
// 2
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
}, 100)
// 3
process.nextTick(function() {
console.log('6');
})
// 4
new Promise(function(resolve) {
console.log('7');
resolve();
console.log('8');
}).then(function() {
console.log('9')
})
// 5
setTimeout(function() {
console.log('10');
process.nextTick(function() {
console.log('11');
})
new Promise(function(resolve) {
console.log('12');
resolve();
}).then(function() {
console.log('13')
})
}, 200)
// 6
console.log('14')
// 首先输出1, 然后遇到setTimeout 放到 宏任务队列中,
// 再遇到nextTick, 放到微任务队列中
// 再遇到Promise 输出 7 之后resolve了, 但是并不影响下面的代码执行,打印8
// 又遇到setTimeout 放入宏任务队列中
// 然后输出 14
// 取出微任务中的事件 打印6
// 取出then微任务 打印 9
// 取出第一个宏任务 打印2
// 遇到nextTick 放入微任务中
// 打印4
// 依次取出微任务队列中的事件 打印 3 5
// 同理 第二个setTimeout 依次执行 10 12 11 13
最终结果 1 7 8 14 6 9 2 4 3 5 10 12 11 13
如果你把这个执行顺序搞明白了,那么就可以说你是基本理解同步任务和异步任务了。