当前位置: 首页 > 后端技术 > Node.js

从promise、process.nextTick、setTimeout开始,说说EventLoop中的Jobqueue

时间:2023-04-03 15:48:24 Node.js

在原文的基础上,在事件循环中加入了一点参考资料。循环读取任务,如例1:setTimeout(function(){console.log(1)},0);console.log(2)//输出2,1在上面的例子中,我们了解到主线程是先执行的主线程中的同步任务是在主线程任务执行完后从事件循环中读取任务,所以输出先2,再输出1。事件循环读取任务的顺序取决于作业队列中不同任务读取规则的限制。例如下面的例子:例子2:setTimeout(function(){console.log(3);},0);Promise.resolve().then(function(){console.log(2);});安慰。log(1);//输出是123先输出1,没问题,因为同步任务是在主线程中先执行的,这里的问题是如何定义setTimeout和Promise.then任务的执行优先级。2.Job队列中的执行顺序Job队列中有两种类型的队列:macro-task和microTask。下面举个例子来看看执行顺序的规律。我们设置宏任务队列包含任务:a1、a2、a3,微任务队列包含任务:b1、b2、b3。a1任务执行完毕后,微任务队列中的任务全部执行完毕,即依次执行b1、b2、b3。执行完后,将micro-task中的task清空,然后执行marco-task中的第二个task,依次循环。了解了宏任务队列和微任务队列的执行顺序之后,我们再来看一下真??实场景中这两类队列中实际包含的任务(我们以节点V8引擎为例),在节点V8中,实际这两类任务的顺序如下:宏任务队列实际包含任务:script(主程序代码)、setTimeout、setInterval、setImmediate、I/O、UI渲染微任务队列实际包含任务:process。nextTick,Promises,Object.observe,MutationObserver我们由此得到的执行顺序应该是:script(主程序代码)—>process.nextTick—>Promises...——>setTimeout—>setInterval—>setImmediate——>I/O——>UI渲染在ES6中,宏任务队列又叫ScriptJobs,微任务又叫PromiseJobs3。真实环境下执行顺序示例(一)setTimeout和promise示例3:setTimeout(function(){console.log(3);},0);Promise.resolve().then(function(){console.log(2);});console.log(1);我们以第1节中的例子为例,这里遵循的顺序是:脚本(主程序代码)-->promise-->setTimeout对应的输出依次为:1-->2--->3(2)进程.nextTick和promise,setTimeout示例4:setTimeout(function(){console.log(1)},0);newPromise(function(resolve,reject){console.log(2);resolve();})。然后(function(){console.log(3)}).then(function(){console.log(4)});process.nextTick(function(){console.log(5)});console.log(6);//输出2,6,5,3,4,1的例子比较复杂,这里需要注意的地方在定义在做一个promise的时候,promise的构造部分是同步执行的,所以问题很容易解决。首先分析Job队列的执行顺序:script(主程序代码)——>process.nextTick——>promise——>setTimeoutI)Mainpart:DefinitionPromise的构造部分是同步的,所以先输出2,然后主要部分输出6(在同步的情况下,严格按照定义的顺序)II)process.nextTick:输出5III)promise:这里的promise部分,严格来说其实就是thepromise.then部分,输出为3,4IV)setTimeout:最后输出1合成的执行顺序为:2——>6——>5——>3——>4——>1(3)复杂的例子setTimeout(function(){console.log(1)},0);newPromise(function(resolve,reject){console.log(2);setTimeout(function(){resolve()},0)}).then(function(){console.log(3)}).then(function(){console.log(4)});process.nextTick(function(){console.log(5)});console.log(6);//输出为265134这种情况和我们在(2)中的例子不同,不同的是在promise的构造中,没有同步resolve,所以promise.then当前执行的队列中不存在,只有当promise从pending转到resolve时,才会有then方法,这个resolve在一个setTimout时间内完成,所以最终输出3,4。知识点参考:process.nextTick(callback)process.nextTick()方法向“下一个报价队列”添加回调。当前事件轮询队列中的任务全部完成后,将依次调用下一个tick队列中的所有回调。此方法不是setTimeout(fn,0)的别名。它更有效率。事件循环对滴答的后续调用在任何I/O事件(包括计时器)之前运行。每次事件轮询后,将首先执行下一个滴答队列,然后再执行其他I/O。调用nextTick回调会递归地阻止任何I/O操作,就像while(true)循环一样。functiondefinitelyAsync(arg,cb){if(arg){process.nextTick(cb);返回;}fs.stat('文件',cb);//涉及到io操作}//这里process.nextTick会阻塞io操作