了解Javascript的eventLoop机制
# 了解Javascript的eventLoop机制
# 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交互
# 一些常见异步容易犯错的点
# 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)
})
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即可;