macrotask 和 microtask

作者:某猫    发布于:

任务队列

当一个异步操作完成时都可以触发一个回调函数,这个回调函数会放入到一个任务队列中,然后等待同步操作完成时再执行任务队列中的待被执行的回调函数。

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
(3)一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

两个任务队列

首先看下面的代码

setTimeout(function() {console.log(4)}, 0);
    new Promise(function executor(resolve) {
        console.log(1);
        for( var i=0 ; i<10000 ; i++ ) {
            i == 9999 && resolve();
        }
        console.log(2);
    }).then(function() {
        console.log(5);
    });
    console.log(3);

如果是按一个任务队列的方式想的话可能会得到这样的答案:12345;
然而实际上是输出:12354;
所以看出Promise.resolve()之后then中的方法同样是异步的但是优先于早在第一行代码就进入任务队列中的计时器的回调函数,这是因为任务队列是分为两个:macrotask 和 microtask,而不同的队列的优先级是不同的。

macrotask:也称为task,包含了script(整体代码),setTimeout, setInterval, setImmediate, I/O, UI rendering
microtask:process.nextTick, Promises, Object.observe, MutationObserver

一个事件循环(EventLoop)中会有一个正在执行的任务(Task),而这个任务就是从 macrotask 队列中来的。在whatwg规范中有 queue 就是任务队列。当这个 macrotask 执行结束后所有可用的 microtask 将会在同一个事件循环中执行,当这些 microtask 执行结束后还能继续添加 microtask 一直到真个 microtask 队列执行结束。

执行过程如下:

1、选出macrotask中最早的任务(macrotask中的第一个回调函数)
2、如果 macrotask 为null (那任务队列就是空),跳到第6步
3、将 currently running task 设置为 macrotask中的第一个回调函数
4、执行回调函数
5、将 currently running task 设置为 null 并移出 执行完回调函数
6、执行 microtask 队列
a: 在 microtask 中选出最早的任务 task X
b: 如果 task X 为null (那 microtask 队列就是空),直接跳到 g
c: 将 currently running task 设置为 task X
d: 执行 task X
e: 将 currently running task 设置为 null 并移出 task X
f: 在 microtask 中选出最早的任务 , 跳到 b
g: 结束 microtask 队列
跳到第一步

那么看一下段代码:

console.log('start')

const interval = setInterval(() => {  
  console.log('setInterval')
}, 0)

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

Promise.resolve()
    .then(() => {  
        console.log('promise 1')
    })
    .then(() => {
        console.log('promise 2')
    })

输出:

start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
promise 5
promise 6

*注:当首轮执行时,存放在script标签中的就是要执行的代码。

format_list_numbered

(无)

  1. 1. 任务队列
  2. 2. 两个任务队列
vertical_align_top

Copyright © 2017 某猫のBlog

Powered by Hexo & Theme - Vateral