了解Javascript的eventLoop机制

7/21/2021 EventLoop

# 了解Javascript的eventLoop机制

  1. # Javascript的eventLoop机制概念

最近刷了几道eventLoop机制的题,await加promise加setTimeOut,每次都觉得对流程理解的差不多了。但是验证时偶尔还会和结果不太一样,说白了还是对每一个语法理解的机理不够深,这次把涉及到的知识点总结一下,留着需要时拿出来看看。

# 为什么会有event Loop

首先,Javascript是单线程非阻塞的。单线程好理解,因为不可能让两个线程同时操作一个Dom;
非阻塞写过JS代码的人都知道,当程序中有一段异步服务请求时,jS执行到这个地方不会等待请求执行完,而是继续是执行下面的代码,请求执行完会在主线程空闲时再去调用这个异步服务的回调函数。所以Javascript是单线程非阻塞的。

# 1.2 浏览器的event Loop机制

其实上一节在介绍event Loop的原由时描述的非阻塞场景,就是土话版的event Loop,这里需要换成JS的语言来描述。
执行栈:上述JS代码在初始执行时,同一环境内的所有同步的代码会入栈,代码执行完则会出栈;
任务队列:在同步代码入栈的过程中,可能会遇到异步代码的调用,这些异步代码返回的结果则不会去执行栈,而是进入到一个叫做“任务队列”的queue中。
当环境中检测到所有的同步代码都已出栈,这时才会调用任务队列中的异步任务回调;而异步任务回调时会把此环境下所有的同步代码放到执行栈中,代码执行完出栈;当代码全部执行完,又会去取任务队列中下一个异步任务回调。。。
可以看到,上述过程会不断重复循环,直到执行栈和任务队列中不再有任何一个事件,这个循环过程就是浏览器中的eventLoop机制;
这里为什么强调了是浏览器中的event Loop机制,是因为node.js中有自己的一套event Loop(有自己的Process.nextTick 最优先级异步任务),但是大致原理是与浏览器这套相同。本篇文章主要总结的是浏览器下的事件循环机制。

# 1.3 macrotask 和 microtask && 任务和微任务

因为任务队列执行的是先进先出的原则,Javascript其实是不能控制某个事件的优先执行顺序的。那能不能有一种机制让某些任务先执行呢?
这就用到了microtask(微任务),于是乎一条任务队列现在变成了两条:微任务队列和任务队列。在这里只需要记住最重要的一条规则:
有微任务永远先执行微任务,微任务的优先级要高于任务。
微任务的异步回调有:Promise, async await, MutationObserver
任务的异步回调有:setTimeOut, setInterval, setImmediate, postMessage, I/O, UI交互

  1. # 一些常见异步容易犯错的点

# 2.1 Promise的执行机理

Promise 主函数内执行的是同步代码,then里面执行的是异步代码,且只有遇到resolve,才会触发then;

new Promise(function(resolve, reject) {
    console.log('1');
    setTimeout(function() {
        console.log('2');
        resolve('3')
    }, 0)
}).then((res) => {
    console.log('4');
    setTimeout(() => {
        console.log(res);
    }, 0)
})
1
2
3
4
5
6
7
8
9
10
11
12
  • 因为Promise主函数内是同步代码,所以先执行1
  • 遇到setTimeout,将console.log('2')和resolve('3')放入macrotask中
  • 因为只有遇到resolve,才会触发then,所以同步代码执行完毕,microtask也没有任务入列,则执行macrotask
  • 执行玩resolve('3')将then放入microtask,则执行resolve('4')
  • 遇到setTimeout,放到macrotask,此时microtask没有任务入列,则执行console.log('2')

# 2.2 async await的执行机理

async await其实是Promise的语法糖,只需记住await跟随的语句是同步代码,要入执行栈;后面的代码要进入microtask即可;