javascript事件循环的task和microtask

编程入门 行业动态 更新时间:2024-10-25 18:27:00

javascript<a href=https://www.elefans.com/category/jswz/34/1770959.html style=事件循环的task和microtask"/>

javascript事件循环的task和microtask

原文地址:.html

首先来看一个示例代码(也是面试时经常会被提及的setTimeout和Promise谁先执行这个问题):

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

正确的输出顺序是:‘script start’、‘script end’、‘promise1’、‘promise2’、‘setTimeout’(即Promise会比setTimeout先执行)。接下来解释一下原理。

每一个“线程”都有一个独立的event loop(即事件循环),每一个web worker也有一个独立的event loop,所以它可以独立的运行(不过web worker并不能操作dom,所以严格意义上来说它并不能让javascript支持多线程,对web worker不了解的同学可以忽略它)。如果没有使用web worker,那么所有的窗口都将共享一个event loop,即使它们可以同步的通信。event loop将会持续不断的、有序的执行队列中的任务(tasks)。每一个event loop都有着众多不同的任务来源(task source),这些task source能够保证其中的task能够有序的执行。不过,在每一轮事件循环结束之后,浏览器可以自行选择将哪一个source当中的task加入到执行队列当中。这样也就使得了浏览器可以优先选择那些敏感性的任务,例如用户的的输入。

这里笔者补充一点:每一个task source(任务来源)都有一个对应的task queue(即任务队列),每次event loop会按优先级载入task queue并按顺序执行task queue里的task,这里有两点要注意:

  1. 同任务来源的task会放在同一个task queue中,浏览器会在一个event loop里执行完一个task queue里的所有task,然后才载入优先级次高的task queue并执行;
  2. 浏览器会控制task source的优先级(或者说task queue的优先级),通常影响用户体验的task会优先执行(如eventListener会比setTimeout优先级高,这很容易理解:用户的点击行为能立刻得到响应当然比setTimeout比实际慢了几百毫秒来的重要)。

Task 是严格按照时间顺序压栈和执行的,所以浏览器能够使得JavaScript内部任务与DOM任务能够有序的执行。当一个task执行结束后,在下一个task执行开始前,浏览器可以对页面进行重新渲染。每一个task都是需要分配的,例如从用户的点击操作到一个点击事件,渲染HTML文档,同时还有上面例子中的setTimeout。

常见的task包括:setTimeout、setInterval、ajax、eventListener等等。==

setTimeout的工作原理相信大家应该都知道,其中的延迟并不是完全精确的,这是因为setTimeout它会在延迟时间结束后分配一个新的task至event loop中,而不是立即执行,所以setTimeout的回调函数会等待前面的task都执行结束后再运行。这就是为什么’setTimeout’会输出在’script end’之后,因为’script end’是第一个task的其中一部分,而’setTimeout’则是一个新的task。这里我们先解释了event loop的基本原理,接下来我们会通过这个来讲解microtask的工作原理。

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

每当一个Promise被决议(或是被拒绝),便会将其回调函数添加至microtask任务队列中作为一个新的microtask。这也保证了Promise可以异步的执行。所以当我们调用.then(resolve, reject)的时候,会立即生成一个新的microtask添加至队列中,这就是为什么上面的’promise1’和’promise2’会输出在’script end’之后,因为microtask任务队列中的任务必须等待当前task执行结束后再执行,而’promise1’和’promise2’输出在’setTimeout’之前,这是因为’setTimeout’是一个新的task,而microtask执行在当前task结束之后,下一个task开始之前。

这里笔者补充一点:microtask同样也有microtask queue,且microtask queue是在当前task queue执行完成后立即执行(而不是每个task执行完成后立即执行对应的microtask)。另外,一个event loop可以执行多个不同的task queue,但microtask queue只有一个,因为microtask queue是在当前task queue执行时生成的,在下一个task queue执行时会清空并重新生成。

更多推荐

javascript事件循环的task和microtask

本文发布于:2024-03-09 08:53:48,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1724528.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:事件   javascript   microtask   task

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!