一、前言
JavaScript是单线程语言,也就是说,只有一条通道,且js中任务是按顺序依次执行的,但若有一个任务时间过长,就会让后续任务一直等待。为了解决这个问题,将任务分为同步任务和异步任务,异步任务又分为宏任务和微任务。
二、同步任务和异步任务
1、同步任务
又叫做非耗时任务,指的是在主线程排队执行的任务。只有前一个任务执行完毕,后一个才可执行。
2、异步任务
又叫做耗时任务,异步任务由JavaScript委托给宿主环境进行执行。
3、执行过程
1)同步任务由JavaScript主线程依次执行,异步任务委托给宿主环境执行
2)已完成的异步任务对应的回调函数加入到任务队列中等待执行
3)JavaScript主线程中被清空后,读取任务队列的回调函数,依次执行
4)JavaScript主线程不断重复以上几步。这个过程是循环不断的,所以这种运行机制被称为EventLoop(事件循环)
三、宏任务和微任务
1、宏任务和微任务有哪些
宏任务:异步Ajax请求,setTimeout,setInterval,文件操作,new Promise等
微任务:Promise.then,Promise.catch,Promise.finally,process.nextTick等
2、宏任务和微任务的执行过程
先执行同步代码,遇到异步宏任务将其放入宏任务队列中,遇到异步微任务将其放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环至所有任务执行完毕。
四、setTimeout()到底是异步的还是同步的
首先,让我们执行这样一段代码:
console.log('111');
setTimeout(()=>{
console.log('222')
},1000);
console.log('333');
setTimeout(()=>{
console.log('444')
},0);
console.log(555);
打印结果为:
可以看出,setTimeout并没有阻塞主线程,所以不是同步执行。第二个setTimeout虽然时间为0,但是仍然在1、3、5之后执行的,所以也明显不是异步。
任务队列除了放置异步任务,也可以放置定时事件。setTimeout()是将事件插入了任务队列,必须等当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。
综上所述,setTimeout类似异步,但不是异步。
异步的三种实现方式:
1)回调函数(回调函数不一定是异步,但异步一定有回调函数)
2)事件
3)promise对象
五、任务的执行过程实例详解
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')
})
})
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')
})
})
1)执行第一行的同步任务,打印1
2)第一个setTimeout加入任务队列宏任务区,记为s1
3)第15行的process.nextTick是异步任务中的微任务,加入微任务区,记为n1
4)第18行的promise是同步任务,打印7,其后的.then是微任务,加入微任务区,记为t1
5)第24行的setTimeout加入任务队列宏任务区,记为s2。至此,同步代码执行完毕。
6)执行微任务区中的任务,n1打印6,t1打印8。至此,微任务区任务执行完成
7)执行宏任务区s1,打印2,其内process.nextTick放入微任务区,记为n2,其后promise为同步任务,立即执行打印4,将后续.then微任务放入微任务区记为t2
8)继续执行微任务区,n2打印3,t2打印5
9)执行第二个setTimeout,直接打印9,其内process.nextTick放入微任务区,记为n3,其后promise为同步任务,立即执行打印11,将后续.then微任务放入微任务区记为t3
10)执行微任务,n3打印10,t3打印12
最终打印结果为:1、7、6、8、2、4、3、5、9、11、10、12