javascript是单线程语言,也就是说代码只能一个接一个地被顺序处理。可奇怪的是既然是单线程,那它是怎么做到异步呢?

我们就来一起研究一下,javascript的运行机制和异步的实现



console.log(1);

setTimeout(()=>{
console.log(2);
new Promise((resolve)=>{
console.log(3);
resolve()
}).then(()=>{
console.log(4)
})
})

new Promise((resolve)=>{
console.log(5);
resolve()
}).then(()=>{
console.log(6)
})

setTimeout(()=>{
console.log(7)
})


我们一起来看一下这段代码的执行结果。(由于浏览器和node环境的运行略有不同,这里按浏览器输出为准)



1
5
6
2
3
4
7


在分析为什么会得出这个结果前,我们需要先了解几个概念

1.宏任务和微任务



而宏任务一般是:包括整体代码script,setTimeout,setInterval、setImmediate。

微任务:原生Promise(有些实现的promise将then方法放到了宏任务中)、process.nextTick、 MutationObserver 。

 

2.事件队列

队列是一种数据结构,主要特征就是先进先出,例如一个只能在最后添加数据,在头部取数据的数组。事件队列是存放宏任务事件的队列。

 

3.事件表

执行代码过程中,遇到异步事件,会把回调事件注册在事件表中,到需要执行回调事件时,将事件表中的回调事件移入事件队列中

 

4.微任务事件队列(MicroTask)

存放微任务事件的队列

 

然后分析一下一开始的题目

1.执行console.log(1);----------------->输出1

2.将setTimeout的回调注册到事件表中,因为没有设置时间,所以移入事件队列中;

3.执行promise;--------------------->输出5,将回调移入微任务队列中

4.将setTimeout的回调注册到事件表中,因为没有设置时间,所以移入事件队列中;

5.第一轮执行完成

6.执行微任务队列中的所有任务----------->输出6

7.在事件队列中移出事件执行------------->执行第一个setTimeout的回调事件

8.执行console.log(2);------------------>输出2

9.执行promise;--------------------->输出3,将回调移入微任务队列中

10.第二轮执行完成

11.执行微任务队列中的所有任务----------->输出4

12.在事件队列中移出事件执行------------->执行第二个setTimeout的回调事件

 13.执行console.log(7)------------------->输出7

所以结果为


1562347

还有需要注意的是,每当执行完一次宏任务,都会将微任务队列中的全部任务执行,执行后再执行下一个宏任务。所以promise.then要比setTimeout要先执行