复习一下之前看过Event loop
首先说一下为啥有这个东西.
因为JavaScript是单线程的,也就是只能顺序执行,当时当我们某一个点需要的等待的时间比较长的时候,那我们的页面就会卡顿,下面的逻辑以及处理也都会进行等待,这个时候就有了Event loop的加入,
js为什么是单线程的?
通俗一点来讲就是:js更多的是和用户进行交互,那如果再一个计划没有执行完,再次执行了下一个计划,而第二个计划又依赖与第一个计划的时候,那我们的页面就会出现问题,给用户不完美的体验
有了它(Event loop)我们能做什么?
举个例子:当我们去请求数据的时候,犹豫后台反应慢或者数据大,那我们再请求的时候所花费的时间就会比较长,那我们再请求的时候能做其它的吗?答案当然是可以了,借用之前以为大佬说过的话,没有代码实现不了的,有,就是你接触的还不够深.
Javascript是单线程的,单线程就意味着所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果其中一个任务很慢,占用了很多的时间,此时网页就可能卡住
有些 I/O (输入输出) 操作是很慢的,比如 Ajax 操作从网络读取数据
JS 语言的设计者意识到,主线程可以不管这些 I / O 操作,把等待中的任务挂起,先运行排在后面的任务。等待 I / O 操作返回结果,再去执行挂起的任务
因此任务可以分为两种,一种是同步任务,一种是异步任务
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
异步任务指的是,不进入主线程,而是进入任务队列,通过 Event Loop 机制等待合适的时间调用
有了 Event Loop 的加持,JS 才能非阻塞地运行
以上是js的Event loop,
那么浏览器是否有自己的Event loop呢?答案也是有的,
浏览器的Event loop
首先看一张图
先别被这张图吓坏,看看执行一个 JavaScript 代码的具体流程
- 执行全局 Script 代码,这些代码中有同步语句或异步语句,遇到同步语句直接执行,异步语句放入宏任务或微任务的队列。
- 全局 Script 代码执行完毕后,调用栈 Stack 会清空
- 从微任务中取出位于队首的回调任务,放入调用栈 Stack 中执行,执行完成后 微任务队列长度减一
- 继续取出位于队首的任务,放入调用栈 Stack 中执行,以此类推,直到把 微任务队列 中的所有任务都执行完毕。注意,如果在执行微任务过程中,又产生了新的微任务,那么会加入到微任务队列的尾部,也会在这个周期被执行
- 当 微任务队列 中的所有任务都执行完毕后,此时 微任务队列 为空,调用栈 Stack 也会空
- 取出宏任务中的队首的任务放入 Stack 中执行
- 执行完毕后,调用栈Stack为空
- 重复第3-7个步骤
- 重复第3-7个步骤
- ...
可以看到,这就是浏览器的事件循环 Event Loop
这里归纳3个重点:
- 宏任务一次只从队列中取一个任务执行,执行完后就去执行微任务队列中的任务;
- 微任务队列中所有的任务都会被依次取出来执行,直到 微任务队列 为空;
- 图中没有画UI rendering的节点,因为这个是由浏览器自行判断决定的,但是只要执行UI rendering,它的节点是在执行完所有的 微任务 之后,下一个 宏任务 之前,紧跟着执行UI render。
下面看一下小例子:
console.log(1); setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3) }); }); new Promise((resolve, reject) => { resolve(4) }).then((data) => { console.log(data); }) setTimeout(() => { console.log(5); }) console.log(6);
以上建议先自己看一下,然后再看答案,
以上执行顺序:
console.log(1);这个应该是没有疑问的吧,第一个执行,它再最顶端 并且是属于微任务的.
然后呢碰到了setTimeout;这个属于宏任务,滚到一边去
接下来是new Promise;这个属于微任务里面没有微任务,加入队列
new Promise().then属于宏任务,滚到一边
setTimeout属于宏任务,滚到一边
console.log(6);微任务 立即执行
到这里所有的微任务执行完毕,开始执行宏任务
拿到第一个setTimeout,里面有微任务,执行,微任务执行完执行宏任务
然后只剩下一个setTimeout
最后执行顺序:1.6.4.2.3.5
总结:
- JavaScript 是单线程的,有了 Event Loop 的加持,JS 才可以非阻塞地执行
- 浏览器的 Event Loop 机制,分为 宏任务 和 微任务
- 微任务比宏任务优先级高,事件循环开始后,微任务首先执行,直到微任务队列清空,然后执行宏任务队列,如果宏任务队列执行过程中,产生了新的微任务,需要立刻执行微任务,直到微任务队列清空,然继续执行宏任务,直到宏任务队列清空
- 宏任务: setTimeout、setInterval、requestAnimation(浏览器)、IO、UI rendering
- 微任务: process.nextTick(Node)、Promise、Object.observe、MutationObserver