JS异步之事件循环机制

一、单线程为什么会有异步

说到js的单线程(single threaded)和异步(asynchronous),这不是自相矛盾么?其实,单线程和异步确实不能同时成为一个语言的特性。js选择了成为单线程的语言,所以它本身不可能是异步的,但js的宿主环境(比如浏览器,Node)是多线程的,宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性,虽然有webworker酱紫的多线程出现,但也是在主线程的控制下。webworker仅仅能进行计算任务,不能操作DOM,所以本质上还是单线程。简单的来说就是JS是单线程,但是它可以向宿主环境请求宿主环境的线程来帮助它,在完成后又把结果返回给它。

在这里插入图片描述

二、事件循环

如上图为事件循环示例图(或JS运行机制图),流程如下:


step1:主线程读取JS代码,此时为同步环境,形成相应的堆和执行栈;


step2: 主线程遇到异步任务,指给对应的异步进程进行处理(WEB API);


step3: 异步进程处理完毕(Ajax返回、DOM事件处罚、Timer到等),将相应的异步任务推入任务队列;


step4:主线程查询任务队列,执行microtask queue,将其按序执行,全部执行完毕;


step5:主线程查询任务队列,执行macrotask queue,取队首任务执行,执行完毕;


step6:重复step4、step5。


简而言之:同步环境执行 -> 事件循环1(microtask queue的All)-> 事件循环2(macrotask queue中的一个) -> 事件循环1(microtask queue的All)-> 事件循环2(macrotask queue中的一个)…


利用microtask queue可以形成一个同步执行的环境,但如果Microtask queue太长,将导致Macrotask任务长时间执行不了,最终导致用户I/O无响应等,所以使用需慎重。

三、任务队列

在这里插入图片描述

3.1、任务队列的类型


 任务队列存在两种类型,一种为microtask queue,另一种为macrotask queue。


 macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick

3.2、两者的区别


 microtask queue:唯一,整个事件循环当中,仅存在一个;执行为同步,同一个事件循环中的microtask会按队列顺序,串行执行完毕;


 macrotask queue:不唯一,存在一定的优先级(用户I/O部分优先级更高);异步执行,同一事件循环中,只执行一个。

在这里插入图片描述 总结:我们把一轮事件循环定义为一个宏任务执行完,然后执行现在微任务队列中所有的微任务,所以事件循环就是不断的完成一个宏任务,然后在完成当前微任务队列中的所有任务。