精华内容
参与话题
问答
  • 宏任务与微任务

    千次阅读 多人点赞 2018-10-29 17:45:44
    之前写过关于事件循环机制的文章 js 的并发模型一文,当时以为已经讲清楚所有这方面的概念了,但是最近又发现,事件循环机制还有宏任务与微任务这个概念没有涉及,所以这里延续之前的文章,再继续讲一讲。

    宏任务与微任务

    之前写过关于事件循环机制的文章 js 的并发模型一文,当时以为已经讲清楚所有这方面的概念了,但是最近又发现,事件循环机制还有宏任务与微任务这个概念没有涉及,所以这里延续之前的文章,再继续讲一讲。

    概念

    在之前的博客里已经讲清楚了事件循环机制是如何运行的(js 的并发模型 地址)。

    当时是这么理解的,当执行引擎在主线程方法执行完毕,到达空闲状态时,会从任务队列中按顺序获取任务来执行(task-> task-> task…)

    这里补充一个概念,浏览器为了能够使得 JS 内部 task(任务) 与 DOM 任务能够有序的执行,会在一个 task 执行结束后,在下一个 task 执行开始前,对页面进行重新渲染 (task-> 渲染-> task->…)

    宏任务(task):就是上述的 JS 内部(任务队列里)的任务,严格按照时间顺序压栈和执行。如 setTimeOut、setInverter、setImmediate 、 MessageChannel等

    微任务(Microtask ):通常来说就是需要在当前 task 执行结束后立即执行的任务,例如需要对一系列的任务做出回应,或者是需要异步的执行任务而又不需要分配一个新的 task,这样便可以减小一点性能的开销。microtask 队列是一个与 task 队列相互独立的队列,microtask 将会在每一个 task 执行结束之后执行。每一个 task 中产生的 microtask 都将会添加到 microtask 队列中,将会添加至当前 microtask 队列的尾部,并且 microtask 会按序的处理完队列中的所有任务,然后开始执行下一个 task 。microtask 类型的任务目前包括了 MutationObserver 以及 Promise 的回调函数。

    或许看完还是一脸懵逼,所以还是得分析例子

    例子

    例子1:

    console.log("script start");
    Promise.resolve().then(function(){
    	console.log("promise1")
    })
    setTimeout(function(){
        console.log("setTimeout")
    },0);
    Promise.resolve().then(function(){
    	console.log("promise2")
    })
    console.log("script end");
    
    //输出
    //script start
    //script end
    //promise1
    //promise2
    //setTimeout
    

    在这里插入图片描述

    (1)当前 JS 代码进入主线程被 JS 引擎执行,当前是一个宏任务。按序执行,先输出script start

    (2) 接着执行 Promise.resolve(1),该回调进入微任务

    (3)执行 setTimeout,回调进入宏任务(这个宏任务是下一个宏任务,而不是当前宏任务)

    (4)执行Promise.resolve(2),该回调进入微任务

    (5)继续执行,输出script end,当前宏任务执行完毕。检测微任务列表

    (6)执行微任务列表,按顺序输出 promise1 promise2

    (7)当前微任务列表为空,渲染 DOM 后执行下一宏任务,即 setTimeout 回调函数,输出 setTimeout

    例子2:

    console.log('script start');
    Promise.resolve().then(function() {
      	setTimeout(function() {
          console.log('setTimeout1');
        }, 0);
    }).then(function() {
      console.log('promise1');
    });
    
    setTimeout(function() {
    	console.log('setTimeout2')
    	Promise.resolve().then(function(){
    		console.log('promise2');
    	})
    },0)
    console.log('script end');
    
    //输出:
    //script start
    //script end
    //promise1
    //setTimeout2
    //promise2
    //setTimeout1
    

    在这里插入图片描述

    (1)进入宏任务,执行当前代码,输出 script start

    (2)执行 Promise.resolve(1),回调(内含函数 setTimeout(1))进入微任务

    (3)执行 setTimeout(2),回调(内含函数 Promise.resolve(2)) 进入下一宏任务

    (4)输出 script end,当前宏任务结束。

    (5)宏任务结束,查看微任务队列,当前微任务是 Promise.resolve(1) 回调,执行回调里面的 setTimeout(1),定时器回调进入宏任务,接着输出 promise1,当前微任务为空

    (6)当前微任务为空,执行下一宏任务。当前宏任务是 setTimeout(2)回调,输出 setTimeout2,执行回调里面的 Promise.resolve(2),回调进入微任务,当前宏任务结束

    (7)当前宏任务结束,执行微任务。输出 Promise2,微任务为空。

    (8)执行下一宏任务,setTimeout1 回调输出 setTimeout1

    可以画图帮助理解,弄清楚上面这个流程基本上就理解这个知识了

    当然有时候有些浏览器并不是这么表现,例如会把 Promise.resolve 当成当成宏任务处理,但是都是特殊情况,因为这样消耗会变很大,如上面流程显示才是最标准的。

    最后结合上并发模型博客里讲的定时器线程再来一个例子仅供玩耍

    console.log('script start');
    Promise.resolve().then(function() {
      	setTimeout(function() {
          console.log('setTimeout1');
        }, 0);
    }).then(function() {
      console.log('promise1');
    });
    
    setTimeout(function() {
    	console.log('setTimeout2')
    	Promise.resolve().then(function(){
    		console.log('promise2');
    	})
    },3000)
    console.log('script end');
    
    //输出:
    //script start
    //script end
    //promise1
    //setTimeout1
    //setTimeout2
    //promise2
    
    展开全文
  • js宏任务与微任务

    千次阅读 2019-04-24 17:14:54
    当我们打开网站时,网页的渲染过程就是一大堆同步任务,比如页面骨架和页面元素的渲染。而像加载图片音乐之类占用资源大耗时久的任务,就是异步任务。关于这部分有严格的文字定义,但本文的目的是用最小的学习成本...

    当我们打开网站时,网页的渲染过程就是一大堆同步任务,比如页面骨架和页面元素的渲染。而像加载图片音乐之类占用资源大耗时久的任务,就是异步任务。关于这部分有严格的文字定义,但本文的目的是用最小的学习成本彻底弄懂执行机制,所以我们用导图来说明:

     

    导图要表达的内容用文字来表述的话:

    • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
    • 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
    • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
    • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

    我们不禁要问了,那怎么知道主线程执行栈为空啊?js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

     

    setTimeout(function() {
        console.log('setTimeout');
    },0)
    new Promise(function(resolve) {
        console.log('promise');
        resolve()
    }).then(function() {
        console.log('then');
    })
      console.log('console');
    
    
    /** 输出
    promise
    console
    then
    setTimeout
    
    */
    • 这段代码作为宏任务,进入主线程。
    • 先遇到setTimeout,那么将其回调函数注册后分发到宏任务Event Queue。(注册过程与上同,下文不再描述)
    • 接下来遇到了Promisenew Promise立即执行,then函数分发到微任务Event Queue。
    • 遇到console.log(),立即执行。
    • 好啦,整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了then在微任务Event Queue里面,执行。
    • ok,第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event Queue中setTimeout对应的回调函数,立即执行。
    • 结束。
    setTimeout(function() {
       new Promise(function(resolve) {
        console.log('promise');
        resolve()
    }).then(function() {
        console.log('then');
    })
    },0)
    setTimeout(function() {
        console.log(1);
    },0)
    
    
    
    /**
    promise 
    then
    1
    */

     

     

    3.又爱又恨的setTimeout

    大名鼎鼎的setTimeout无需再多言,大家对他的第一印象就是异步可以延时执行,我们经常这么实现延时3秒执行:

    setTimeout(() => {
        task()
    },3000)
    
    sleep(10000000)

    乍一看其实差不多嘛,但我们把这段代码在chrome执行一下,却发现控制台执行task()需要的时间远远超过3秒,说好的延时三秒,为啥现在需要这么长时间啊?

    这时候我们需要重新理解setTimeout的定义。我们先说上述代码是怎么执行的:

    • task()进入Event Table并注册,计时开始。
    • 执行sleep函数,很慢,非常慢,计时仍在继续。
    • 3秒到了,计时事件timeout完成,task()进入Event Queue,但是sleep也太慢了吧,还没执行完,只好等着。
    • sleep终于执行完了,task()终于从Event Queue进入了主线程执行。

    上述的流程走完,我们知道setTimeout这个函数,是经过指定时间后,把要执行的任务(本例中为task())加入到Event Queue中,又因为是单线程任务要一个一个执行,如果前面的任务需要的时间太久,那么只能等着,导致真正的延迟时间远远大于3秒。

     

    展开全文
  • js 宏任务与微任务

    2020-03-11 00:23:24
    宏任务微任务 宏任务 macro-task 大概包括: ...process.nextTick(普通微任务有区别,在微任务队列执行之前执行) new Promise().then(回调)等。 先执行整体script代码,如果遇到宏任务,放入...

    宏任务和微任务

    宏任务 macro-task 大概包括:

    setTimeout
    setInterval
    setImmediate
    script(整体代码)
    I/O 操作等。

    微任务 micro-task 大概包括:

    process.nextTick(与普通微任务有区别,在微任务队列执行之前执行)
    new Promise().then(回调)等。

    先执行整体script代码,如果遇到宏任务,放入宏队列,如果遇到微任务,放进微队列,主进程代码执行完后,进去微任务,执行完所有的微任务(如果中途遇到新的微任务,放到当前队列尾部执行),执行完所有微任务后,找到刚才的宏队列,按照顺序一个一个取出执行,下面上代码:

    
            setTimeout(function () {
                console.log('3')
                new Promise(resolve => {
                    console.log('4')
                    resolve()
                })
                    .then(function () {
                        console.log('5')
                    })
            }, 0)
    
            new Promise(resolve => {
                console.log('1')
                resolve()
            })
                .then(function () {
                    console.log('2')
                })
    
            setTimeout(function () {
                console.log('6')
            }, 0)
    

    一开始进来,遇到setimeout(宏任务),放进宏队列,这里成为setimeout1号。
    接下来执行new Promise里面的(console.log(‘1’)),然后遇到then(微任务),执行console.log(‘2’)。
    接下来执行最下面的setTimeout(宏任务),放入宏队列,成为setimeout2号。
    接下来没有微任务了,需要从刚才的宏队列取出一个宏任务执行。这里当然是取setimeout1号啦。
    执行setimeout1号,首先执行 console.log(‘3’),然后执行 new Promise,执行 console.log(‘4’),然后执行then(微任务),执行 console.log(‘5’)。本宏任务结束,寻找下一个宏任务。
    执行setimeout2号,输出 console.log(‘6’)。
    到这里就结束啦!
    注意,如果遇到await函数,会把后面的语句归为微任务,并跳出当前async函数!如下图:
    在这里插入图片描述
    输出顺序为 4,1,3,6,8,2,7,5

    展开全文
  • JS宏任务与微任务

    2020-01-20 09:36:47
    最近发现关于JS运行机制的宏任务与微任务有的小伙伴还不是特别清楚,今天我们就好好讲一下,希望大家可以彻底理解,废话少说,先看东西 如果有人问你什么是宏任务什么是微任务,这样告诉他,多说一个字都是多余 宏任务:...

    最近发现关于JS运行机制的宏任务与微任务有的小伙伴还不是特别清楚,今天我们就好好讲一下,希望大家可以彻底理解,废话少说,先看东西

    如果有人问你什么是宏任务什么是微任务,这样告诉他,多说一个字都是多余

    宏任务:包括整段代码script,setTimeout,setInterval

    微任务:Promise,process.nextTick

    不同类型的任务会进入不同的事件队列,就是我们所说的宏任务队列和微任务队列。我们知道js执行是有顺序的,那到底是什么决定了这个顺序呢?就是事件循环的顺序。首先进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。

    setTimeout(function() {
        console.log('setTimeout');
    })
    
    new Promise(function(resolve) {
        console.log('promise');
    }).then(function() {
        console.log('then');
    })
    
    console.log('console');
    1. 整段代码作为宏任务进入主线程
    2. 遇到settimeout,将其回调函数注册后分发到宏任务Event Queue。
    3. 遇到了Promise,new Promise立即执行,then函数分发到微任务Event Queue
    4. 遇到console.log(),立即执行
    5. 第一个宏任务执行结束,看看有什么微任务,发现有then,执行
    6. 第二轮循环,发现宏任务settimeout的回调函数,执行。
    7. 结束。
    console.log('1');
    
    setTimeout(function() {
        console.log('2');
        process.nextTick(function() {
            console.log('3');
        })
        new Promise(function(resolve) {
            console.log('4');
            resolve();
        }).then(function() {
            console.log('5')
        })
    })
    process.nextTick(function() {
        console.log('6');
    })
    new Promise(function(resolve) {
        console.log('7');
        resolve();
    }).then(function() {
        console.log('8')
    })
    
    setTimeout(function() {
        console.log('9');
        process.nextTick(function() {
            console.log('10');
        })
        new Promise(function(resolve) {
            console.log('11');
            resolve();
        }).then(function() {
            console.log('12')
        })
    })

    答案:1 7 6 8 、2 4 3 5、 9 11 10 12

    展开全文
  • 怎么说呢emmm,浏览器的event loop的任务队列分为宏任务(macrotask)与微任务(microtask),两者的执行优先级(或者说顺序不同) 题为转载 作者:xiaohesong链接:...
  • 这两天学习了一下js事件轮询机制,看了很多文章,有的人说宏任务先执行,有的人说微任务先执行。看下面例子 console.log('1'); setTimeout(function() { console.log('2'); new Promise(function(resolve)...
  • 宏任务一般是:包括整体代码script,setTimeout,setInterval。 微任务:Promise,process.nextTick。 a主线任务 异步队列,先执行: b微任务, c最宏任务。 27.前端跨域的方式 ​ 使用jsonp跨域,因为script标签...
  • 在介绍前端宏任务与微任务之前,先列出来一道题,一块看一下。console.log('1') setTimeout(() => { console.log('2') }) new Promise((resolve, rejects) => { console.log('3') resolve() }).then(() =&...
  • 宏任务macrotask有哪些 script setTimeout setInterval setImmediate requestAnimationFrame I/O操作 UI渲染 微任务microtask有哪些 process.nextTick MutationObserver Promise.then 执行优先级 主线程script> ...
  • 宏任务与微任务面试题 对于宏任务与微任务的执行顺序问题一直都是前端面试一到过不去的坎,这东西多看是没用的,多做就是了,刷了10来到面试题,立马顿悟。 1 async function async1 () { console.log('async1...

空空如也

1 2 3 4 5 ... 18
收藏数 360
精华内容 144
关键字:

宏任务与微任务