宏任务与微任务

前言

·什么是宏观任务,什么是微观任务?

宏任务:宿主(浏览器)发起的任务我们可以称之为宏观任务(macrotask) script、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)
微任务:引擎(js)自己发起的任务叫做微观任务(microtask) promise async await

·js执行宏任务和微任务的联系:

先执行宏任务再执行当前宏任务内的微任务(在此过程中将遇到的微宏任务依次推入到eventqueue, 先进入eventqueue的任务会被先执行),执行完当前宏任务内的所有微任务之后,再执行下一个宏任务, 以此循环,当然在微宏任务中同步任务会优先执行,而整体的scrpt标签就是第一个执行的宏任务
·Promoise相关
Promise中的异步体现在then和catch中,所以写在Promise中的代码是被当做同步任务立即执行的。 而在async/await中,在出现await出现之前,其中的代码也是立即执行的实际上await是一个让出线程的标志。 await后面的表达式会先执行一遍,将await后面的代码加入到microtask(微观任务)中

·示例(最近看到的经典题)


image.png
题解:
1.script是第一个宏任务

2.先执行当前宏观里的所有同步任务(主线程任务),再执行当前宏观里的所有微观任务,再执行下一个宏观任务,
即script宏(同->微)->下一个宏(同->微)->....

同步任务队列 console.log(4)-> async1()-> async2()-> console.log(8)

console.log(4); 同步任务 所以第一步打印4

async1(); 返回的是一个promise,promise本身是同步,所以 第二步打印1,并且将await之后的放到微观任务队列

async2(); 同步任务打印3,

promise();promise.then之前同步任务打印6,并将promise.then异步任务放到微观任务队列

console.log(8);同步任务打印8

微观任务队列 consol.log(2)-> console.log(7)

consol.log(2) 微观任务打印2

console.log(7) 微观任务打印7

宏观任务 setTimeout

setTimeout(function(){

console.log(5) 宏观任务打印5

})

打印顺序

同步4->同步1->同步3->同步6->同步8->微观2->微观7->宏观->5

image.png

·其它案例(来小试牛刀一下吧)

题一:

async function async1() {
    console.log('async1 start')
    await async2()
    console.log('async1 end')
  }
  async function async2() {
    new Promise(resolve => {
        console.log('promise1')
        resolve()
      })
      .then(() => {
        console.log('promise2')
      })
  }
  console.log('script start')
  setTimeout(() => {
    console.log('setTimeout')
  }, 0);
  async1()
  new Promise(resolve => {
      console.log('promise3')
      resolve()
    })
    .then(() => {
      console.log('promise4')
    })
  console.log('script end')
  
题二:

Promise.resolve().then(()=>{
  console.log('Promise1')  
  setTimeout(()=>{
    console.log('setTimeout2')
  },0)
})
setTimeout(()=>{
  console.log('setTimeout1')
  Promise.resolve().then(()=>{
    console.log('Promise2')    
  })
},0)

题三:

async function async1() {
  console.log('async1 start')
  await async2()
  setTimeout(() => {
    console.log('setTimeout1')
  }, 0)
}
async function async2() {
  setTimeout(() => {
    console.log('setTimeout2')
  }, 0)
}
console.log('script start')
setTimeout(() => {
  console.log('setTimeout3')
}, 0);
async1()
new Promise(resolve => {
    console.log('promise1')
    resolve()
  })
  .then(() => {
    console.log('promise2')
  })

1."script start"
  "async1 start"
  "promise1"
  "promise3"
  "script end"
  "promise2"
  "async1 end"
  "promise4"
  "setTimeout"
 2.
  "Promise1"
  "setTimeout1"
  "Promise2"
  "setTimeout2"
 3.
  "script start"
  "async1 start"
  "promise1"
  "script end"
  "promise2"
  "setTimeout3"
  "setTimeout2"
  "setTimeout1"


链接:https://juejin.cn/post/7057453597415440414

事件循环

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,防止主线程的不阻塞,Event Loop 的方案应用而生。Event Loop 包含两类:一类是基于 Browsing Context,一种是基于 Worker。二者的运行是独立的,也就是说,每一个 JavaScript 运行的"线程环境"都有一个独立的 Event Loop,每一个 Web Worker 也有一个独立的 Event Loop。

本文所涉及到的事件循环是基于 Browsing Context。

任务队列

根据规范,事件循环是通过任务队列的机制来进行协调的。一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。setTimeout/Promise 等API便是任务源,而进入任务队列的是他们指定的具体执行任务。

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:

  • 在此次 tick 中选择最先进入队列的任务(oldest task),如果有则执行(一次)
  • 检查是否存在 Microtasks,如果存在则不停地执行,直至清空 Microtasks Queue
  • 更新 render
  • 主线程重复执行上述步骤

在上诉tick的基础上需要了解几点:

  • JS分为同步任务和异步任务
  • 同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
  • 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
image.png

宏任务

(macro)task,可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。

浏览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个(macro)task执行结束后,在下一个(macro)task 执行开始前,对页面进行重新渲染,流程如下:

(macro)task->渲染->(macro)task->...

宏任务包含:

script(整体代码)
setTimeout
setInterval
I/O
UI交互事件
postMessage
MessageChannel
setImmediate(Node.js 环境)

微任务

microtask,可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。

所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。

微任务包含:

Promise.then
Object.observe
MutationObserver
process.nextTick(Node.js 环境)

运行机制

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:

  • 执行一个宏任务(栈中没有就从事件队列中获取)
  • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
  • 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

如图:


image.png

例子

{ // script宏
  Promise.resolve().then(()=>{ // 微 异步
    console.log('第一个回调函数:微任务1')
    setTimeout(()=>{ // 宏
      console.log('第三个回调函数:宏任务2')
    },0)
  })
  setTimeout(()=>{ // 宏
    console.log('第二个回调函数:宏任务3')
    Promise.resolve().then(()=>{ // 微
      console.log('第四个回调函数:微任务4')
    })
  },0)
  // script > [<microtask微>Promise.then(微任务1)],[<macrotask宏>setTimeout(宏任务3)]
  // > Promise.then(微任务1) > [<macrotask宏>setTimeout(宏任务3),setTimeout(宏任务2)]
  // > setTimeout(宏任务3) > [<microtask微>Promise.then(微任务4)],[<macrotask宏>setTimeout(宏任务2)]
  // > Promise.then(微任务4) > [<macrotask宏>setTimeout(宏任务2)]
  // > setTimeout(宏任务2)



  // 第一个回调函数:微任务1
  // 第二个回调函数:宏任务3
  // 第四个回调函数:微任务4
  // 第三个回调函数:宏任务2
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容