javascript 事件循环

引自:总是一知半解的Event Loop

阮一峰老师的这篇文章挺不错:http://www.ruanyifeng.com/blog/2014/10/event-loop.html

一、关于MacroTask(宏观任务)和MicroTask(微观任务)

macroTask和microTask是两种任务队列。
大家更熟悉的关于事件循环的机制说法大概是:主进程执行完了之后,每次从任务队列里取一个任务执行。

1、JavaScript引擎对这两种队列有不同的分类:

  • macroTask:

    • setTimeout,
    • setInterval,
    • setImmediate,
    • requestAnimationFrame,
    • I/O,
    • UI rendering
  • microTask:

    • process.nextTick,
    • Promise,
    • Object.observe,
    • MutationObserver

我们所熟悉的定时器就属于macroTask

microTask 在 macroTask 之前执行

image.png

2、根据上图,Node.js的运行机制如下。

(1)V8引擎解析JavaScript脚本。
(2)解析后的代码,调用Node API。
(3)libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
(4)V8引擎再将结果返回给用户。

process.nextTick方法可以在___当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")之前___----触发回调函数。
也就是说,它指定的任务总是发生在所有异步任务之前
setImmediate方法则是在___当前"任务队列"的尾部___添加事件,也就是说,它指定的任务总是在下一次Event Loop,异步任务之时执行,这与setTimeout(fn, 0)很像。

看代码:

process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0)
// 1
// 2
// TIMEOUT FIRED

我们以setTimeout、process.nextTick、promise为例直观感受下两种任务队列的运行方式。

console.log('main1');  // 主进程-->输出顺序:1
process.nextTick(function() { 
      console.log('process.nextTick1');  // microTask-->输出顺序:4
});
setTimeout(function() { 
    console.log('setTimeout');  // macroTask-->输出顺序:6
    process.nextTick(function() { 
        console.log('process.nextTick2'); // macroTask 执行后定义的另一个新的 microTask-->输出顺序:7
    });
}, 0);
new Promise(function(resolve, reject) { 
    console.log('promise');   // 主进程-->输出顺序:2
    resolve();
}).then(function() { 
    console.log('promise then'); // microTask-->输出顺序:5
});
console.log('main2');  // 主进程-->输出顺序:3

别着急看答案,先以上面的理论自己想想,运行结果会是啥?

最终结果是这样的:

main1
promise
main2
process.nextTick1
promise then
setTimeout
process.nextTick2

process.nextTick和 promise then在 setTimeout前面输出,已经证明了macroTask和microTask的执行顺序。

第一个循环里,process.nextTick1Promise.then这两个microTask是在setTimeout这个macroTask里之前输出的,这是为什么呢?

因为主进程的代码也属于macroTask(理论支撑:【翻译】Promises/A+规范)。

主进程这个macroTask(也就是main1promisemain2)执行完了,自然会去执行process.nextTick1Promise.then这两个microTask
这是第一个循环

之后的setTimeoutprocess.nextTick2属于第二个循环

重点说下UI rendering。在HTML规范:event-loop-processing-model里叙述了一次事件循环的处理过程,在处理了macroTask和microTask之后,会进行一次Update the rendering,其中细节比较多,总的来说会进行一次UI的重新渲染。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容