js中的宏任务与微任务
如果你已经知道了js中存在宏任务和微任务,那么你一定已经了解过promise了。因为在js中promise是微任务的一个入口。
先来看一道题:
setTimeout(function(){
console.log('setTimeout')
});
new Promise(function(resolve, reject){
console.log('pormise body');
resolve();
}).then(function(){
console.log('promise then')
});
console.log('main');
这题的答案是:
promise body
main
promise then
setTimeout
promise body出现在第一行一点也不意外,意外的是,setTimeout出现在了promise then的后边。
setTimeout和promise then都是异步调用,setTimeout又定义在promise之前,如果没有意外,应该是先输出setTimeout才对,但这里却恰恰相反。
这里涉及到的知识点,就是宏任务与微任务了。标记一下上边的代码:
setTimeout(function(){ // 同步代码,语句1
console.log('setTimeout') // 宏任务,语句2
});
new Promise(function(resolve, reject){
console.log('pormise body'); // 同步代码,语句3
resolve(); // 同步代码,语句4
}).then(function(){
console.log('promise then') // 微任务,语句5
});
console.log('main'); // 同步代码,语句6
那么他们运行的规则是怎样的呢?
原来,宏任务与微任务,都各自有一个调用队列(先进先出)。
遇到宏任务,微任务,则将他们推入各自的调用队列。需要注意的是,同步代码也是宏任务。
宏任务执行完一个,则去清空微任务队列,微任务清空后再去执行下一个宏任务。
这个过程颇像去医院诊室排队看大夫的情景:
如果有一个病人A叫到号以后,又被大夫安排先去做检查,那么做完检查以后病人A回到诊室门口,可以直接等待当前正在看病的病人B与大夫交谈结束后,将检查结果交给大夫,而不用再次排队。
我们来把上面的代码一行一行解读一下:
首先定义两个队列,宏任务队列:MacroQqueue, 微任务队列: MicroQueue
第一步,先按同步代码顺序运行
同步代码,语句1: 添加一个宏任务,将语句2推入MacroQueue。 // MacroQueue: [{task: 语句2}]
同步代码,语句3: 打印promise body
同步代码,语句4: 添加一个微任务,将语句5推入MicroQueue。 // MicroQueue: [{task: 语句5}]
同步代码,语句6: 打印main。 // 同步代码(宏任务)完成
第二步,开始清空微任务队列
微任务: 语句5,从MicroQueue跳出,打印promise then。 // 微任务队列全部清空
第三步,开始清空宏任务队列
宏任务:语句2,从MacroQqueue跳出,打印setTimeout // 宏任务队列全部清空
第四步:开始清空微任务队列
队列为空...
一轮循环完成。开始下一轮,如此循环下去。
通过上面的讲解,大家应该能对宏任务,微任务的运行机制有了一定的了解了吧。那么都有哪些常见的宏任务与微任务呢?
请看下表:
| 宏任务 | 浏览器 | nodejs |
|---|---|---|
| 同步代码 | ✅ | ✅ |
| I/O | ✅ | ✅ |
| setTimeout | ✅ | ✅ |
| setInterval | ✅ | ✅ |
| setImmediate | ❌ | ✅ |
| requestAnimationFrame | ✅ | ❌ |
| 微任务 | 浏览器 | nodejs |
|---|---|---|
| process.nextTick | ❌ | ✅ |
| MutationObserver | ✅ | ❌ |
| Promise (async/await) | ✅ | ✅ |
好了,我们来一道复杂一点的题练练手:
console.log('sync statement 1');
Promise.resolve().then(function() {
console.log('micro task 1');
setTimeout(function() {
console.log('macro task 1');
}, 0);
}).then(function() {
console.log('micro task 2');
});
setTimeout(function() {
console.log('macro task 2')
Promise.resolve().then(function(){
console.log('micro task 3');
})
}, 0)
console.log('sync statement 2');
//输出:
// sync statement 1
// sync statement 2
// micro task 1
// micro task 2
// macro task 2
// micro task 3
// macro task 1
标注一下方便大家分析:
console.log('sync statement 1'); // 同步代码,语句1
Promise.resolve().then(function() { // 同步代码,语句2,注册了一个微任务
console.log('micro task 1'); // 微任务,语句3
setTimeout(function() { // 微任务,语句4,同时注册了一个宏任务
console.log('macro task 1'); // 宏任务,语句5
}, 0);
}).then(function() {
console.log('micro task 2'); // 微任务,语句6
});
setTimeout(function() { // 同步代码,语句7
console.log('macro task 2') // 宏任务,语句8
Promise.resolve().then(function(){ // 宏任务,语句9,同时注册了一个微任务
console.log('micro task 3'); // 微任务,语句10
})
}, 0)
console.log('sync statement 2'); // 同步代码,语句11
js中的宏任务与微任务的更多相关文章
- javascript中的宏任务和微任务(一)
一.宏任务和微任务有哪些 宏任务:setTimeout,setInterval,ajax,dom,宏任务是由浏览器提供的 微任务:promise,async/await,微任务是由es6提供的 二.微 ...
- javascript中的宏任务和微任务(二)
js事件轮询执行顺序总结: 1)所有的同步任务都在主线程上执行,行成一个执行栈. 2)除了主线程之外,还存在一个任务列队,只要异步任务有了运行结果,就在任务列队中植入一个时间标记. 3)主线程完成所有 ...
- JS异步之宏队列与微队列
1. 原理图 2. 说明 JS 中用来存储待执行回调函数的队列包含 2 个不同特定的列队 宏列队:用来保存待执行的宏任务(回调),比如:定时器回调.DOM 事件回调.ajax 回调 微列队:用来保存待 ...
- JS中EventLoop、宏任务与微任务的个人理解
为什么要EventLoop? JS 作为浏览器脚本语言,为了避免复杂的同步问题(例如用户操作事件以及操作DOM),这就决定了被设计成单线程语言,而且也将会一直保持是单线程的.而在单线程中若是遇到了耗时 ...
- JavaScript同步模式,异步模式及宏任务,微任务队列
首先JavaScript是单线程的语言,也就是说JS执行环境中,负责执行代码的线程只有一个.一次只能执行一个任务,如果有多个任务的话, 就要排队,然后依次执行,优点就是更安全,更简单.缺点就是遇到耗时 ...
- JS中的执行机制(setTimeout、setInterval、promise、宏任务、微任务)
1.执行机制 JS 是单线程的,处理 JS 任务(程序)只能一个一个顺序执行,所以 JS 中就把任务分为了同步任务和异步任务.同步的进入主线程先执行,异步的进入Event Table并注册函数,当指定 ...
- js的事件循环机制:同步与异步任务(setTimeout,setInterval)宏任务,微任务(Promise,process.nextTick)
javascript是单线程,一切javascript版的"多线程"都是用单线程模拟出来的,通过事件循环(event loop)实现的异步. javascript事件循环 事件循环 ...
- js 宏任务和微任务
.宏任务(macrotask )和微任务(microtask ) macrotask 和 microtask 表示异步任务的两种分类. 在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首 ...
- js 同步 异步 宏任务 微任务 文章分享
分享一篇 写的很好的 宏任务 微任务 同步异步的文章 文章原地址: https://juejin.im/post/59e85eebf265da430d571f89 这一次,彻底弄懂 JavaScri ...
随机推荐
- 使用 Github + Hexo 从 0 搭建一个博客
最近有几位同学在公众号后台留言问我的博客站是怎么建站的,思来想去,还是写一篇从 0 开始吧. 前置准备 我们先聊一下前置准备,可能很多同学一听说要自己搭一个博客系统,直接就望而却步.不得有台服务器么, ...
- [考试反思]1105csp-s模拟测试102: 贪婪
还是有点蠢... 多测没清空T3挂40...(只得了人口普查分20) 多测题要把样例复制粘两遍自测一下防止未清空出锅. 然而不算分... 其实到现在了算不算也不重要了吧... 而且其实T3只考虑最长路 ...
- 差异:后缀数组(wzz模板理解),单调栈
因为涉及到对模板的理解,所以就着代码看会好一些. 让那些坚决不颓代码的人受委屈了. 我是对着wzz的板子默写的,可能不完全一样啊. 还有代码注释里都是我个人的理解,不保证正确,但欢迎指正. 可以有选择 ...
- 微服务架构~Zuul1.0和2.0我们该如何选择?
介绍 在今年5月中,Netflix终于开源了它的支持异步调用模式的Zuul网关2.0版本,真可谓千呼万唤始出来.从Netflix的官方博文[附录1]中,我们获得的信息也比较令人振奋: The Clou ...
- ElasticSearch(四):基本搜索
ElasticSearch(四):基本搜索 学习课程链接<Elasticsearch核心技术与实战> URI Search 使用HTTP的GET方法,在URL中使用查询参数进行查询. GE ...
- 利用DI实现级联删除 - xms跨平台基础框架 - 基于.netcore
一.引言 所谓级联删除是指删除一条记录后,附带关联记录也一起删除,比如删除客户后,联系人也一起删除: 以往我们会依赖于数据库表的外键约束,但存在着明显的问题,增加数据库压力.提示不友好.职责越界.事务 ...
- go中的数据结构切片-slice
1.部分基本类型 go中的类型与c的相似,常用类型有一个特例:byte类型,即字节类型,长度为,默认值是0: bytes = []btye{'h', 'e', 'l', 'l', 'o'} 变量byt ...
- python——直接插入排序
直接插入排序 直接插入排序原理 直接插入排序过程 代码 时间复杂度分析 排序稳定性
- win7 安装php插件imagick
win7 安装php插件imagick <h2>安装步骤:</h2><h2><a name="t1"></a> ...
- java多线程回顾2:生命周期与控制
1. 线程生命周期概述 线程的生命周期如下图: 2. 新建与就绪 当程序使用new关键字创建一个线程之后,线程就处于新建状态了.此时线程只是被分配了内存资源,初始化了成员变量. 当线程对象被调用了 ...