如果你已经知道了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中的宏任务与微任务的更多相关文章

  1. javascript中的宏任务和微任务(一)

    一.宏任务和微任务有哪些 宏任务:setTimeout,setInterval,ajax,dom,宏任务是由浏览器提供的 微任务:promise,async/await,微任务是由es6提供的 二.微 ...

  2. javascript中的宏任务和微任务(二)

    js事件轮询执行顺序总结: 1)所有的同步任务都在主线程上执行,行成一个执行栈. 2)除了主线程之外,还存在一个任务列队,只要异步任务有了运行结果,就在任务列队中植入一个时间标记. 3)主线程完成所有 ...

  3. JS异步之宏队列与微队列

    1. 原理图 2. 说明 JS 中用来存储待执行回调函数的队列包含 2 个不同特定的列队 宏列队:用来保存待执行的宏任务(回调),比如:定时器回调.DOM 事件回调.ajax 回调 微列队:用来保存待 ...

  4. JS中EventLoop、宏任务与微任务的个人理解

    为什么要EventLoop? JS 作为浏览器脚本语言,为了避免复杂的同步问题(例如用户操作事件以及操作DOM),这就决定了被设计成单线程语言,而且也将会一直保持是单线程的.而在单线程中若是遇到了耗时 ...

  5. JavaScript同步模式,异步模式及宏任务,微任务队列

    首先JavaScript是单线程的语言,也就是说JS执行环境中,负责执行代码的线程只有一个.一次只能执行一个任务,如果有多个任务的话, 就要排队,然后依次执行,优点就是更安全,更简单.缺点就是遇到耗时 ...

  6. JS中的执行机制(setTimeout、setInterval、promise、宏任务、微任务)

    1.执行机制 JS 是单线程的,处理 JS 任务(程序)只能一个一个顺序执行,所以 JS 中就把任务分为了同步任务和异步任务.同步的进入主线程先执行,异步的进入Event Table并注册函数,当指定 ...

  7. js的事件循环机制:同步与异步任务(setTimeout,setInterval)宏任务,微任务(Promise,process.nextTick)

    javascript是单线程,一切javascript版的"多线程"都是用单线程模拟出来的,通过事件循环(event loop)实现的异步. javascript事件循环 事件循环 ...

  8. js 宏任务和微任务

    .宏任务(macrotask )和微任务(microtask ) macrotask 和 microtask 表示异步任务的两种分类. 在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首 ...

  9. js 同步 异步 宏任务 微任务 文章分享

    分享一篇 写的很好的 宏任务 微任务  同步异步的文章 文章原地址: https://juejin.im/post/59e85eebf265da430d571f89 这一次,彻底弄懂 JavaScri ...

随机推荐

  1. 学习笔记34_EF上下文管理

    *上下文对象dbContext最好不要频繁的使用Using(var dbContext = new ....):那么就会产生过多的数据库交互:而且每个dbContext中村的数据,由于操作不同,数据可 ...

  2. [爬虫]用python的requests模块爬取糗事百科段子

    虽然Python的标准库中 urllib2 模块已经包含了平常我们使用的大多数功能,但是它的 API 使用起来让人感觉不太好,而 Requests 自称 “HTTP for Humans”,说明使用更 ...

  3. 水管局长数据加强版:lct,时光倒流,最小生成树,边化点

    Description: SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到 ...

  4. [07/18NOIP模拟测试5]超级树

    鬼能想到的dp定义:dp[i][j]表示在一棵i级超级树中,有j条路径同时存在且这j条路径没有公共点时,可能的情况数 刚开始我也没看懂,所以举个例子 如一个2级的超级树,父节点为1,左右儿子为2,3 ...

  5. Vue计算属性缓存(computed) vs 方法

    Vue计算属性缓存(computed) vs 方法 实例 <div id="example"> <p>Original message: "{{ ...

  6. P1041 传染病控制(noip2003)(搜索)

    呃呃呃...真的是惨烈啊... 今天的模拟赛是真的惨..... 本题,正解居然是搜索!!!!!! 蒟蒻自己歪歪了一个貌似是正解但是却连一半都没过的错解. 先解释一下自己的dp思路把. $f[i][u] ...

  7. PTA刷题记录(3)

    团队天梯赛-------(3)分值:15 给定一个 k 位整数 N=d​k−1​​10​k−1​​+⋯+d​1​​10​1​​+d​0​​ (0≤d​i​​≤9, i=0,⋯,k−1, d​k−1​​ ...

  8. js设置Date

    function getDate (yyyy, MM, dd) { let t = new Date() t.setFullYear(yyyy) t.setMonth(Number(MM) - 1) ...

  9. 你知道MySQL中的主从延迟吗?

    前言 在一个MySQL主备关系中,每个备库接受主库的binlog并执行. 正常情况下,只要主库执行更新生成所有的binlog,都可以传到备库并被正常的执行,这样备库就能够达到跟主库一样的状态,这就是最 ...

  10. 【SQL SERVER】2017 Developer 安装教程

    官网下载地址:https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 1.下载之后双击exe文件,选择基本 自定义都行 2.选择 ...