js的settimeout方法 settimeout函数用法( 三 )


通过结果可以看出,虽然我们先调用的asyncFunc,虽然asyncFunc写的是2秒后执行,但是syncFunc的执行时间太长,达到了5秒,asyncFunc虽然在2秒的时候就已经进入了事件队列,但是主线程一直在执行同步代码,一直没空,所以也要等到5秒后,同步代码执行完毕才有机会执行这个定时器回调 。所以再次强调,写代码时一定不要长时间占用主线程 。
引入微任务前面的流程图我为了便于理解,简化了事件队列,其实事件队列里面的事件还可以分两类:宏任务和微任务 。微任务拥有更高的优先级,当事件循环遍历队列时,先检查微任务队列,如果里面有任务,就全部拿来执行,执行完之后再执行一个宏任务 。执行每个宏任务之前都要检查下微任务队列是否有任务,如果有,优先执行微任务队列 。所以完整的流程图如下:
上图需要注意以下几点:
一个Event Loop可以有一个或多个事件队列,但是只有一个微任务队列 。微任务队列全部执行完会重新渲染一次每个宏任务执行完都会重新渲染一次requestAnimationFrame处于渲染阶段,不在微任务队列,也不在宏任务队列
所以想要知道一个异步API在哪个阶段执行,我们得知道他是宏任务还是微任务 。
常见宏任务有:
script (可以理解为外层同步代码)setTimeout/setIntervalsetImmediate(Node.js)I/OUI事件postMessage
常见微任务有:
Promiseprocess.nextTick(Node.js)Object.observeMutaionObserver
上面这些事件类型中要注意Promise,他是微任务,也就是说他会在定时器前面运行,我们来看个例子:
console.log('1');setTimeout(() => {console.log('2');},0);Promise.resolve().then(() => {console.log('5');})new Promise((resolve) => {console.log('3');resolve();}).then(() => {console.log('4');})上述代码的输出是1,3,5,4,2 。因为:
先输出1,这个没什么说的,同步代码最先执行console.log(‘2’);在setTimeout里面,setTimeout是宏任务,“2”进入宏任务队列console.log(‘5’);在Promise.then里面,进入微任务队列console.log(‘3’);在Promise构造函数的参数里面,这其实是同步代码,直接输出console.log(‘4’);在then里面,他会进入微任务队列,检查事件队列时先执行微任务同步代码运行结果是“1,3”然后检查微任务队列,输出“5,4”最后执行宏任务队列,输出“2”
Node.js的Event LoopNode.js是运行在服务端的js,虽然他也用到了V8引擎,但是他的服务目的和环境不同,导致了他API与原生JS有些区别,他的Event Loop还要处理一些I/O,比如新的网络连接等,所以与浏览器Event Loop也是不一样的 。Node的Event Loop是分阶段的,如下图所示:
timers: 执行setTimeout和setInterval的回调pending callbacks: 执行延迟到下一个循环迭代的 I/O 回调idle, prepare: 仅系统内部使用poll: 检索新的 I/O 事件;执行与 I/O 相关的回调 。事实上除了其他几个阶段处理的事情,其他几乎所有的异步都在这个阶段处理 。check: setImmediate在这里执行close callbacks: 一些关闭的回调函数,如:socket.on(‘close’, …)
每个阶段都有一个自己的先进先出的队列,只有当这个队列的事件执行完或者达到该阶段的上限时,才会进入下一个阶段 。在每次事件循环之间,Node.js都会检查它是否在等待任何一个I/O或者定时器,如果没有的话,程序就关闭退出了 。我们的直观感受就是,如果一个Node程序只有同步代码,你在控制台运行完后,他就自己退出了 。
还有个需要注意的是poll阶段,他后面并不一定每次都是check阶段,poll队列执行完后,如果没有setImmediate但是有定时器到期,他会绕回去执行定时器阶段:


以上关于本文的内容,仅作参考!温馨提示:如遇健康、疾病相关的问题,请您及时就医或请专业人士给予相关指导!

「四川龙网」www.sichuanlong.com小编还为您精选了以下内容,希望对您有所帮助: