event loop 宏队列 微队列

event loop

reference: https://segmentfault.com/a/1190000016278115

event loop是一个执行模型,在不同的地方有不同的实现。浏览器和NodeJS基于不同的技术实现了各自的Event Loop

浏览器的Event Loop是在html5的规范中明确定义。

NodeJS的Event Loop是基于libuv实现的。可以参考Node的官方文档以及libuv的官方文档。

libuv已经对Event Loop做出了实现,而HTML5规范中只是定义了浏览器中Event Loop的模型,具体的实现留给了浏览器厂商。

event loop

1.执行全局script同步代码,这些同步代码有一些是同步语句,有一些是异步语句(比如setTimeout等);

2.全局script代码执行完毕后,调用栈stack会清空;

3.从微队列microtask queue中取出位于队首的回调任务,放入调用栈Stack中执行,执行完后microtask queue长度减1;

4.继续取出位于队首的任务,放入调用栈Stack中执行,以此类推,直到直到把microtask queue中的所有任务都执行完毕。注意,如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行

5.microtask queue中的所有任务都执行完毕,此时microtask queue为空队列,调用栈stack也为空;

6.取出宏队列macrotask queue中位于队首的任务,放入Stack中执行;

7.执行完毕后,调用栈stack为空;

8.重复第3-7个步骤;

9.重复第3-7个步骤;

10. ...


1. 宏队列macrotask一次只从队列中取一个任务执行,执行完后就去执行微任务队列中的任务

2. 微任务队列中所有的任务都会被依次取出来执行,直到microtask queue为空

3. 图中没有画UI rendering的节点,因为这个是由浏览器自行判断决定的,但是只要执行UI rendering,它的节点是在执行完所有的microtask之后,下一个macrotask之前,紧跟着执行UI render。


事件循环机制:宏任务与微任务、执行栈、执行一个宏任务就清空一次微任务队列,然后再执行宏任务

宏队列,macrotask,也叫tasks

一些异步回调会依次进入macro task queue,等待后续被调用,包括:

setTimeout/setInterval

UI rendering (浏览器独有)

requestAnimationFrame (浏览器独有)

I/O

setImmediate (Node独有)


微队列,microtask,也叫jobs

另一些异步回调会依次进入micro task queue,等待后续被调用,包括:

Promise.then()

Object.observe

MutationObserver

process.nextTick (Node独有)


在使用Promise.resolve(value)等方法的时候,如果promise对象立刻就能进入resolve状态的话,那么你是不是觉得.then里面指定的方法就是同步调用的呢?

实际上,.then中指定的方法调用是异步进行的。

var promise=new Promise(resolve=>{

    console.log("inner promise");// 1

    resolve(42);

});

promise.then(value=>{

    console.log(value);// 3

});

console.log("outer promise");// 2

执行上面的代码会输出下面的log,从这些log我们清楚地知道了上面代码的执行顺序。

inner promise // 1

outer promise // 2

42 // 3

由于JavaScript代码会按照文件的从上到下的顺序执行,所以最开始<1>会执行,然后是resolve(42);被执行。这时候promise对象的已经变为确定状态,FulFilled被设置为了42。

下面的代码promise.then注册了<3>这个回调函数,这是本专栏的焦点问题。

由于promise.then执行的时候promise对象已经是确定状态,从程序上说对回调函数进行同步调用也是行得通的。

但是即使在调用promise.then注册回调函数的时候promise对象已经是确定的状态,Promise也会以异步的方式调用该回调函数,这是在Promise设计上的规定方针。

因此<2>会最先被调用,最后才会调用回调函数<3>。


async functionasync1() {

  console.log('async1 start');

  await async2();

  console.log('async1 end');

}

async functionasync2() {

  console.log('async2');

}

console.log('script start');

setTimeout(function() {

    console.log('setTimeout1');

}, 200);

setTimeout(function() {

    console.log('setTimeout2');

    newPromise(function(resolve) {

        resolve();

    }).then(function() {

        console.log('then1')

    })

    newPromise(function(resolve) {

        console.log('Promise1');

        resolve();

    }).then(function() {

        console.log('then2')

    })

},0)

async1();

newPromise(function(resolve) {

    console.log('promise2');

    resolve();

  }).then(function() {

    console.log('then3');

  });

console.log('script end');


// setTimeout是异步任务中的宏任务

setTimeout(() => console.log('a'));


// Promise是异步任务中的微任务,比setTimeout先执行,链式执行法,遇到setTimeout先挂起,往下执行

Promise.resolve().then(

   () => console.log('b')  // 输出 'b'

 ).then(

    //该函数返回值是 'c'

   () => Promise.resolve('c').then(

     (data) => {

       setTimeout(() => console.log('d'));  //挂起,放到宏任务队列之后

       console.log('f');  //执行,输出'f'

       return data;

     })

 ).then(data => console.log(data) ); // 接收到的就是输出 'c'


// 执行宏任务

setTimeout(() => console.log('a'));  //输出 'a'

// 后续添加的宏任务

 setTimeout(() => console.log('d'));  //输出 'd'


new Promise((resolve) => {

console.log('1')

    resolve()

console.log('2')

}).then(() => {

    console.log('3')

})

setTimeout(() => {

console.log('4')

})

console.log('5')

由于javascript是单线程任务所以主线程只能同时处理一个任务,所以把异步的事件放在同步的事件处理完成之后再来依次处理。


同步:console.log(1)->console.log(2)->console.log(5);

异步:(微任务)console.log(3)->(宏任务)console.log(4);


你可能感兴趣的:(event loop 宏队列 微队列)