javaScript macro-task(宏任务)与micro-task(微任务)

弄懂js异步

讲异步之前,我们必须掌握一个基础知识-event-loop。

我们知道JavaScript的一大特点就是单线程,而这个线程中拥有唯一的一个事件循环。当然新标准中的web worker涉及到了多线程,但它的原理是利用一个父线程和多个子线程,归根结底来说js仍逃不过是单线程的事实。

JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)(先进先出)来搞定另外一些代码的执行

一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。

任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。

macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的具体执行任务。

//setTimeout本身是一个任务分发器,他作为一个任务源,会立即执行

setTimeout(function(){

     // 里面的函数是具体任务,它会延迟执行

     console.log('dc');

})

来自不同任务源的任务会进入到不同的任务队列。其中setTimeout与setInterval是同源的。

事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。

其中每一个任务的执行,无论是macro-task还是micro-task,都是借助函数调用栈来完成。

// 为了方便理解,我以打印出来的字符作为当前的任务名称

setTimeout(function(){

      console.log('timeout1');

})

new Promise(function(resolve){

       console.log('promise1');

       for(var i =0; i <1000; i++) {

              i ==99&& resolve();

       }

console.log('promise2');

}).then(function(){

       console.log('then1');

})

console.log('global1')

// promise1

// promise2

// global1

// then1timeout1

首先,事件循环从宏任务队列开始,这个时候,宏任务队列中,只有一个script(整体代码)任务。每一个任务的执行顺序,都依靠函数调用栈来搞定,而当遇到任务源时,则会先分发任务到对应的队列中去,所以,上面例子的第一步执行如下图所示。

首先script任务开始执行,全局上下文入栈

第二步:script任务执行时首先遇到了setTimeout,为一个宏任务源,那么他的作用就是将任务分发到它对应的队列中

宏任务timeout1进入setTimeout队列

第三步:script执行时遇到Promise实例。Promise构造函数中的第一个参数,是在new的时候执行,因此不会进入任何其他的队列,而是直接在当前任务直接执行了,而后续的.then则会被分发到micro-task的Promise队列中去

因此,构造函数执行时,里面的参数进入函数调用栈执行。for循环不会进入任何队列,因此代码会依次执行,所以这里的promise1和promise2会依次输出

promise1入栈执行,这时promise1被最先输出
resolve在for循环中入栈执行
构造函数执行完毕的过程中,resolve执行完毕出栈,promise2输出,promise1也出栈,then执行时,Promise任务then1进入对应队列

script任务继续往下执行,最后只有一句输出了globa1,然后,全局任务就执行完毕了。

第四步:第一个宏任务script执行完毕之后,就开始执行所有的可执行的微任务。这个时候,微任务中,只有Promise队列中的一个任务then1,因此直接执行就行了,执行结果输出then1,当然,他的执行,也是进入函数调用栈中执行的。

执行所有的微任务

第五步:当所有的micro-tast执行完毕之后,表示第一轮的循环就结束了。这个时候就得开始第二轮的循环。第二轮循环仍然从宏任务macro-task开始

微任务被清空

这个时候,我们发现宏任务中,只有在setTimeout队列中还要一个timeout1的任务等待执行。因此就直接执行即可

timeout1入栈执行

这个时候宏任务队列与微任务队列中都没有任务了,所以代码就不会再输出其他东西了。

这个例子比较简答,涉及到的队列任务并不多,因此读懂了它还不能全面的了解到事件循环机制的全貌。所以我下面弄了一个复杂一点的例子,再给大家解析一番,相信读懂之后,事件循环这个问题,再面试中再次被问到就难不倒大家了

console.log('golb1');

setTimeout(function() {

    console.log('timeout1');

    process.nextTick(function() {

        console.log('timeout1_nextTick');

    })

    new Promise(function(resolve) {

        console.log('timeout1_promise');

        resolve();

    }).then(function() {

        console.log('timeout1_then')

    })

})


setImmediate(function() {

    console.log('immediate1');

    process.nextTick(function() {

        console.log('immediate1_nextTick');

    })

    new Promise(function(resolve) {

        console.log('immediate1_promise');

        resolve();

    }).then(function() {

            console.log('immediate1_then')

    })

})


process.nextTick(function() {    

        console.log('glob1_nextTick');

})


new Promise(function(resolve) {

        console.log('glob1_promise');

        resolve();

}).then(function() {

        console.log('glob1_then')

})


setTimeout(function() {

     console.log('timeout2');

    process.nextTick(function() {

          console.log('timeout2_nextTick');

    })

    new Promise(function(resolve) {

        console.log('timeout2_promise');

        resolve();

    }).then(function() {

        console.log('timeout2_then')

    })

})


process.nextTick(function() {

        console.log('glob2_nextTick');

})


new Promise(function(resolve) {

        console.log('glob2_promise');

        resolve();

 }).then(function() {

            console.log('glob2_then')

 })


setImmediate(function() {

    console.log('immediate2');

    process.nextTick(function() {

            console.log('immediate2_nextTick');

    })

    new Promise(function(resolve) {

            console.log('immediate2_promise');

            resolve();

    }).then(function() {

            console.log('immediate2_then')

    })

})

最终结果: 

golb1

glob1_promise

glob2_promise

glob1_nextTick

glob2_nextTick

glob1_then

glob2_then

timeout1

timeout1_promise

timeout1_nextTick

timeout1_then

timeout2

timeout2_promise

timeout2_nextTick

timeout2_then

immediate1

immediate1_promise

immediate1_nextTick

immediate1_then

immediate2

immediate2_promise

immediate2_nextTick

immediate2_then

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

推荐阅读更多精彩内容