js 高级

微任务、宏任务、event loop

  • 宏任务:I/0(用户交互)、setTimeout、setInterval、setImmediate、requestAnimationFrame 、I/O 、UI rendering
  • 微任务:Promise.then catch finally、MutationObserver等、process.nextTick(node)、 Promise为基础开发的其它技术,比如fetch API、V8的垃圾回收过程
  • 任务队列执行的过程:每次宏任务执行完,都去检查是否有微任务存在,如有,依次执行微任务,微任务执行结束后,返回继续执行下一个宏任务;如没有微任务,则结束当前宏任务,继续执行下一宏任务。这个执行过程称为 event loop。

event loop执行顺序

  • 1 一开始整个脚本作为一个宏任务执行
  • 2 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
  • 3 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完
  • 4 执行浏览器UI线程的渲染工作
  • 5 检查是否有Web Worker任务,有则执行
  • 6 执行完本轮的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空

node环境中:

  • process.nextTick 的执行优先级高于 Promise 的
  • setTimeout 的执行优先级高于 setImmediate 的(浏览器中往往正好相反)

参考 1 2

example 1

(function test() {
    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);
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

1,2,3,5,4

Promise.then是异步执行的(微任务),而创建Promise实例(executor)是同步执行的,而then中注册的回调才是异步执行的。

参考: 1

example 2

async function async1() {
    console.log( 'async1 start' )
    await async2()
    console.log( 'async1 end' )
}

async function async2() {
    console.log( 'async2' )
}

console.log( 'script start' )

setTimeout( function () {
    console.log( 'setTimeout' )
}, 0 )

async1();

new Promise( function ( resolve ) {
    console.log( 'promise1' )
    resolve();
} ).then( function () {
    console.log( 'promise2' )
} )

console.log( 'script end' )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

--> 任务队列布局如下:

    script start
    async1 start
    async2
    promise1
    script end
    promise2
    async1 end
    setTimeout
1
2
3
4
5
6
7
8

参考 1

node 中的 eventloop

nodejs的event loop分为6个阶段,它们会按照顺序反复运行,分别如下:

    1. timers:执行setTimeout() 和 setInterval()中到期的callback。
    1. I/O callbacks:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行
    1. idle, prepare:队列的移动,仅内部使用
    1. poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段
    1. check:执行setImmediate的callback
    1. close callbacks:执行close事件的callback,例如socket.on("close",func)

不同于浏览器的是,在每个阶段完成后,而不是MacroTask任务完成后,microTask队列就会被执行。这就导致了同样的代码在不同的上下文环境下会出现不同的结果。 1

setTimeout(()=>{
    console.log('timer1');

    Promise.resolve().then(function() {
        console.log('promise1');
    })
}, 0);

setTimeout(()=>{
    console.log('timer2');

    Promise.resolve().then(function() {
        console.log('promise2');
    })
}, 0);

// 浏览器输出:
// time1
// promise1
// time2
// promise2

// Node输出:
// time1
// time2
// promise1
// promise2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

参考 1

最近更新: 9/13/2020, 10:47:04 PM