JS事件循环与任务队列(异步原理)
让我们先来看下面一道前端面试题:
请问以上代码执行的结果是什么?
如果你想当然的以为执行的结果是1,2的话,那么就中招了,面试官就会知道你不知道JS的异步原理。其实这道题的正确顺序是先打印2再打印1,那么这是为什么呢?
首先我们来看这张图,可以看到图中有三部分,第一部分是Callback Queue(任务队列)
,第二部分是JS引擎
(左边部分),里面有个Heap
和Stack
,第三部分是WebAPI
,包括一些DOM
,ajax
,setTimeOut
等等,这三部分用箭头关联了起来,形成了一个循环。那么这个图是什么意思呢?
一段JS代码刚执行的时候,会有一个主事件,然后会丢到Callback Queue(任务队列)里面,JS引擎会去任务队列里面取一个任务来执行,因为JS是单线程
的,所以它每次只能处理一个事件,在执行这个事件中如果里面有一些异步任务,比如说DOM操作,ajax,还有setTimeOut等等,它就会丢给WebAPI来执行,而且它丢完之后它就不管了,它跟WebAPI说:“你先执行着吧,我就不管这摊事儿了,我继续执行后面的代码”,那么WebAPI在执行这些异步任务结束的时候,它会把回调函数里面的js代码再次放到Callback Queue里面,我们都知道异步任务都是有回调函数的,比如说DOM操作的onClick事件,ajax请求的成功或失败,还有setTimeOut的多少毫秒后执行的回调函数,它会把回调函数丢到任务队列里面,然后任务队列里面如果前面的事件都执行完了,那么新丢的回调函数的代码就会丢到JS引擎里面执行,如果回调函数里面还有异步任务,那么就会继续作这个循环,这就是事件循环
我们回头看刚才那段代码:
这段代码刚执行的时候会有一个主事件会放到任务队列中,然后JS引擎会去任务队列里面拿到这个事件去执行,执行到setTimeOut这段代码的时候,发现这是一个异步任务,就交给WebAPI来执行这个异步任务,它继续执行下面的代码。WebAPI在0ms之后就执行完了,接着把执行完的回调函数() => console.log(1)这段代码放到任务队列中,但是发现任务队列中还有一个事件没执行完,这个事件就是我们的主事件,我们的主事件为什么没执行完,因为里面还有个console.log(2)没执行,等打印完2之后,主事件执行完了才轮到打印1这段代码,所以最后的顺序是:先执行console.log(2)
再执行console.log(1)
只有你理解了事件循环,你才能理解为什么刚才那段代码先执行console.log(2)
再执行console.log(1)