浏览器中的 Event Loop
当我们执行 JS 代码的时候其实就是往执行栈中放入函数,那么遇到异步代码的时候该怎么办?其实当遇到异步的代码时,会被挂起并在需要执行的时候加入到 Task(有多种 Task) 队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步还是同步行为。

不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobs ,macrotask 称为 task 。下面来看以下代码的执行顺序:
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('script end')
// script start => async2 end => Promise => script end => promise1 => promise2 => async1 end => setTimeout
注意:新的浏览器中不是如上打印的,因为 await 变快了,具体内容可以往下看
首先先来解释下上述代码的 async 和 await 的执行顺序。当我们调用 async1 函数时,会马上输出 async2 end,并且函数返回一个 Promise,接下来在遇到 await 的时候会就让出线程开始执行 async1 外的代码,所以我们完全可以把 await 看成是让出线程的标志。
然后当同步代码全部执行完毕以后,就会去执行所有的异步代码,那么又会回到 await 的位置执行返回的 Promise 的 resolve 函数,这又会把 resolve 丢到微任务队列中,接下来去执行 then 中的回调,当两个 then 中的回调全部执行完毕以后,又会回到 await 的位置处理返回值,这时候你可以看成是 Promise.resolve (返回值).then() ,然后 await 后的代码全部被包裹进了 then 的回调中,所以 console.log(' async1 end ') 会优先执行于 setTimeout。
如果你觉得上面这段解释还是有点绕,那么我把 async 的这两个函数改造成你一定能理解的代码
new Promise((resolve, reject) => {
console.log('async2 end')
// Promise.resolve() 将代码插入微任务队列尾部
// resolve 再次插入微任务队列尾部
resolve(Promise.resolve())
}).then(() => {
console.log('async1 end')
})
也就是说,如果 await 后面跟着 Promise 的话,async1 end 需要等待三个 tick 才能执行到。那么其实这个性能相对来说还是略慢的,所以 V8 团队借鉴了 Node 8 中的一个 Bug,在引擎底层将三次 tick 减少到了二次 tick。但是这种做法其实是违法了规范的,当然规范也是可以更改的,这是 V8 团队的一个 PR,目前已被同意这种做法。
所以 Event Loop 执行顺序如下所示:
- 首先执行同步代码,这属于宏任务
- 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
- 执行所有微任务
- 当执行完所有微任务后,如有必要会渲染页面
- 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数
所以以上代码虽然 setTimeout 写在 Promise 之前,但是因为 Promise 属于微任务而 setTimeout 属于宏任务,所以会有以上的打印。
微任务包括 process.nextTick ,promise ,MutationObserver 。
宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering 。
这里很多人会有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script ,浏览器会先执行一个宏任务,接下来有异步代码的话才会先执行微任务。
转载自:yck的https://juejin.im/book/5bdc715fe51d454e755f75ef/section/5be04a8e6fb9a04a072fd2c
浏览器中的 Event Loop的更多相关文章
- js 在浏览器中的event loop事件队列
目录 前言 认识一个栈两个队列 执行过程 异步任务怎么分配 简单例子 难一点的例子 前言 以下内容是js在浏览器中的事件队列执行,与在nodejs中有所区别,请注意. 都说js是单线程的,不过它本身其 ...
- [NodeJs系列][译]理解NodeJs中的Event Loop、Timers以及process.nextTick()
译者注: 为什么要翻译?其实在翻译这篇文章前,笔者有Google了一下中文翻译,看的不是很明白,所以才有自己翻译的打算,当然能力有限,文中或有错漏,欢迎指正. 文末会有几个小问题,大家不妨一起思考一下 ...
- 不要在nodejs中阻塞event loop
目录 简介 event loop和worker pool event loop和worker pool中的queue 阻塞event loop event loop的时间复杂度 Event Loop中 ...
- 浏览器和Node 中的Event Loop
前言 js与生俱来的就是单线程无阻塞的脚本语言. 作为单线程语言,js代码执行时都只有一个主线程执行任务. 无阻塞的实现依赖于我们要谈的事件循环.eventloop的规范是真的苦涩难懂,仅仅要理解的话 ...
- 详解JavaScript中的Event Loop(事件循环)机制
前言 我们都知道,javascript从诞生之日起就是一门单线程的非阻塞的脚本语言.这是由其最初的用途来决定的:与浏览器交互. 单线程意味着,javascript代码在执行的任何时候,都只有一个主线程 ...
- node.js中对Event Loop事件循环的理解
javascript是单线程的,所以任务的执行都需要排队,任务分为两种,一种是同步任务,一种是异步任务. 同步任务是进入主线程上排队执行的任务,上一个任务执行完了,下一个任务才会执行. 异步任务是不进 ...
- 为什么JS是单线程?JS中的Event Loop(事件循环)?JS如何实现异步?setimeout?
https://segmentfault.com/a/1190000012806637 https://www.jianshu.com/p/93d756db8c81 首先,请牢记2点: (1) JS是 ...
- 浏览器与Node的事件循环(Event Loop)有何区别?
前言 本文我们将会介绍 JS 实现异步的原理,并且了解了在浏览器和 Node 中 Event Loop 其实是不相同的. 一.线程与进程 1. 概念 我们经常说 JS 是单线程执行的,指的是一个进程里 ...
- [转]Event loop——浏览器和Node区别
最近对Event loop比较感兴趣,所以了解了一下.但是发现整个Event loop尽管有很多篇文章,但是没有一篇可以看完就对它所有内容都了解的文章.大部分的文章都只阐述了浏览器或者Node二者之一 ...
随机推荐
- git push 报src refspec xxx does not match any的错误
今天在向一个新的远程分支上推送项目的时候报错: 远程分支branch_new是其他人建的,我在自己本地修改后把自己分支的修改推送到这个远程分支上. 把修改提到本地仓库: git add ./ git ...
- app消息推送
Mui + 个推 实现消息推送 1.首先去个推 注册一个账号,新建一个消息推送应用 2.配置Mui配置文件 3.使用HBuilder 打包 app 4.然后在到个推后台 发送数据 后台Java代码(官 ...
- 6、pytest -- 临时目录和文件
目录 1. 相关的fixture 1.1. tmp_path 1.2. tmp_path_factory 1.3. tmpdir 1.4. tmpdir_factory 1.5. 区别 2. 默认的基 ...
- 关于RocketMQ消息消费与重平衡的一些问题探讨
其实最好的学习方式就是互相交流,最近也有跟网友讨论了一些关于 RocketMQ 消息拉取与重平衡的问题,我姑且在这里写下我的一些总结. ## 关于 push 模式下的消息循环拉取问题 之前发表了一篇关 ...
- Java设计模式:23种设计模式
1. 软件设计模式概述 2. GoF 的 23 种设计模式的分类和功能 3. UML中的类图及类图之间的关系 4. 开闭原则——面向对象设计原则 5. 里氏替换原则——面向对象设计原则 6. 依赖倒置 ...
- 暑期集训20190725 胜地不常(paradise)
[题目描述] 给定两个长度为n的非负整数数组a,b, [输入数据] 第一行一个整数n. 第二行n个整数a1~an. 第三行n个整数b1~bn. [输出数据] 一行一个整数表示答案. [样例输入] 4 ...
- group:状压dp,轮廓线
神仙题.但是难得的傻孩子cbx没有喊题解,所以也就难得的自己想出来了一个如此神仙的题. 如果是自己想的,说它神仙是不是有点不合适啊..? 反正的确不好像.关键就在于这个标签.颓完标签就差不多会了. % ...
- 「卡常」我T死了我好桑心,我不会卡常。
我死了我死了我死了 puts(“Lrefrain==T”)
- CSPS模拟 80
题还没改完就来臭不要脸的写反思了. (主要因为太困了懒得改了) (还因为T2看起来太过弱智) (也许等我生物钟恢复正常后能当做课余消遣水一水) statistic:skyh接了两杯水,真能喝啊 然后他 ...
- LINUX 内核移植以及网卡驱动添加
我用的板子是sama5d3xek,原来板子内核是linux-at91-3.13,升级使用linux-at91-4.10 首先去官网下载一个linux—at91-4.10压缩包,然后在ubuntu里解压 ...