当前位置: 首页 > Web前端 > HTML5

事件循环:微任务和宏任务

时间:2023-04-06 00:15:23 HTML5

事件循环:微任务和宏任务JavaScript的执行流程,无论是浏览器还是Node.js,都是基于事件循环的。了解事件循环可以让我们编写更可靠、更高效的代码。我们先介绍一下事件循环的原理,再来看实际应用。事件循环(EventLoop)事件循环的概念很简单。这是一个无限循环。JavaScript引擎等待任务出现,然后执行任务,执行完继续等待任务出现。JavaScript引擎对事件循环的算法是:当找到一个任务时:执行该任务,等待其他任务从最先进入队列的任务出现,然后执行步骤1。浏览网页时,呈现在这边走。JavaScript引擎大部分时间处于空闲状态,只有在被脚本文件、处理程序和事件系统激活时才会运行。例如:当通过functioncount(){for(leti=0;i<1e6;i++){i++;}进步。innerHTML=我;}}count();...但是假设我们想在任务执行过程中显示一些额外的信息,比如进度条。如果我们通过setTimeout把这些“重”任务一个一个拆分成小任务,那么对i的变化就会不断渲染。例如:

现在,div元素将显示i的递增值,就像一个进度条。用例3:在事件处理程序之后执行某些操作在事件处理程序中,我们可能会延迟执行某些操作,直到事件冒泡并被所有事件阶段处理为止。我们可以通过将某些操作包装在具有0秒延迟的setTimeout中来实现。因为原案例设计有一些额外的知识(CustomeEvent自定义事件),为了简单起见,这里给笔者一个案例:你好
除了上面说的宏任务,还有微任务(microtasks)。需要更准确地管理异步任务。因此,ECMA标准定义了一个内部的promiseJobs队列,通常称为“微任务队列”(ES8术语)。ESMA262中定义:微任务队列是“先进先出”的:第一个进入队列的任务先执行。只有当前没有其他任务在运行时,微任务才会开始执行。简单地说,当一个Promise就绪时,它的.then/catch/finally事件处理程序将被排队;他们暂时不会被处决。只有当JS引擎处理完当前代码后,微任务队列中的任务才会按顺序执行。例如:让promise=Promise.resolve();promise.then(()=>alert("promisedone!"));alert("代码完成");//这个alert显示首先执行上面的代码,codefinished将首先显示,然后是promisedone!。Promise函数将始终按此顺序执行。如果是链式.then/catch/finally,每一项都会异步执行。也就是说,先将它们放入microtask队列中,然后等待当前代码执行完毕,microtask队列中之前的任务执行完毕,再执行。如果需要按顺序执行怎么办?如何确保首先显示承诺完成,然后代码完成?只需通过.then将它们一个一个地放入队列:Promise.resolve().then(()=>alert("promisedone!")).then(()=>alert("codefinished"));微任务来自我们的代码。通常通过Promise创建,.then/catch/finally的处理函数成为一个microtask。这同样适用于await函数,这是处理Promises的另一种方式。另外,可以通过queueMicrotask(func)函数将func函数放入微任务队列中(目前IE不支持)。每个宏任务执行完后,引擎会立即执行微任务队列中的所有任务,然后继续执行其他宏任务或渲染DOM操作。例如:setTimeout(()=>alert("timeout"));Promise.resolve().then(()=>alert("promise"));alert("code");上面代码中的弹出框会按照什么顺序显示呢?先展示代码,因为是普通的同步函数;then显示promise,因为.then在微任务队列中,所以会在当前宏任务执行时执行;最后显示超时,因为它是另一个宏任务。完整的事件循环过程:(从上到下,脚本文件(宏任务)->微任务->渲染操作->重复过程...)在任何其他事件处理程序、渲染操作或其他宏任务执行之前,所有微任务将被执行。如果我们想异步执行一个函数(当前代码执行完之后),但是在DOM操作渲染之前,或者其他事件处理函数和宏任务执行之前,我们可以通过queueMicrotask来设置。另一个进度条的例子:和上面说的类似,但是这里用的是queueMicrotask,而不是setTimeout。每一个宏任务执行完之后,都会进行渲染操作,就好像是一段同步代码:
总结更多关于事件循环算法的细节(相对于事件循环定义,还是很简单的):宏任务中的第一个任务先出队先执行(比如脚本文件));执行所有微任务:当微任务队列不为空时:微任务队列中的第一个任务出队并执行渲染操作(如果DOM被修改);如果宏任务队列为空,则等待宏任务出现;执行步骤1。如果要设置一个新的宏任务:使用0秒延迟setTimeout(f)当一个涉及大量计算的任务被一个一个地拆分成小任务时,设置一个新的宏任务可以让浏览器做用户做的事情响应并显示进度。它还可以在事件处理程序中使用,以便在事件完全处理(事件冒泡完成)时执行操作。如果设置一个新的微任务:使用queueMicrotask(f)。使用Promise,promise任务将在微任务队列中。微任务队列在处理过程中,不会处理任何UI或网络事件,微任务队列中的所有任务都会立即被一个一个执行。所以我们可能使用queueMicrotask来异步执行一个函数,但是当前的环境状态(environmentstate)并没有改变。WebWorkers如果你不想阻塞事件循环,当涉及非常大和复杂的操作时,你可以使用WebWorkers。通过并行线程运行代码WebWorkers可以与主进程交换信息,但它有自己的变量,事件循环WebWorkers无法访问DOM,因此在计算时同时使用多核CPU非常有用。