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

process.nextTick(),setTimeout(),setInterval()运行机制

时间:2023-04-03 13:40:16 Node.js

setTimeout()JavaScript是一种单线程语言,也就是说一次只能执行一段代码。接下来,我们通过两个例子来说明单线程语言和多线程语言有什么区别。setTimeout代码的单线程运行机制:/***setTimeout的执行要等到主线程的进程执行完毕后才会继续执行,按照setTimeout设置的顺序排队执行。*如果某个setTimeout进行了大量的计算,会阻塞在当前的setTimeout回调函数中,等待计算完成,再执行下一个setTimeout回调函数。*/setTimeout(()=>{console.log('setTimeout-a');},0);console.log(1);console.log(2);setTimeout(()=>{for(让我=0;i<10000022200;i++){}console.log('setTimeout-b');},0);console.log(3);setTimeout(()=>{console.log('setTimeout-c');},0);console.log(4);setTimeout(()=>{console.log('setTimeout-d');},0);console.log(5);for(leti=0;i<10000222200;i++){}//setTimeout回调执行完才会执行。从运行结果可以看出,虽然setTimeout-a写在了代码的开头,但是延迟时间同样为0,则不立即执行;主要逻辑代码执行完后调用,当代码运行到第25行时,因为这里有一个长循环,会阻塞等待一段时间,然后再运行到第一个setTimeout。setTimeout的运行顺序由你代码中写的顺序和延迟时间决定。下面用一张图来说明上面代码的运行机制:从运行结果可以看出,如果主要逻辑代码没有执行完,setTimeout回调函数永远不会被触发,这是一个单线程,它可以一次只做一件事。**注意:如果主逻辑的代码执行时间已经超过了setTimeout第二个参数设置的超时时间,那么回调函数运行时,会忽略这个时间,立即执行。下面通过一段代码验证:/***setTimeout的执行要等到主线程的进程执行完毕,才会按照setTimeout设置的顺序排队执行。*如果某个setTimeout进行了大量的计算,会阻塞在当前的setTimeout回调函数中,等待计算完成,再执行下一个setTimeout回调函数。**执行顺序:即使setTimeout在最顶层执行,也不会运行到主线程执行完毕。这就是单线程运行机制。*setTimeout中第二个参数timeout的延迟时间是一个相对时间。如果主线程的运行时间已经超过了这个时间,那么在执行这个setTimeout的时候,这个时间会被忽略,直接调用函数。*/setTimeout(()=>{console.log('setTimeout-a');},0);setTimeout(()=>{for(leti=0;i<10000022200;i++){}console.log('setTimeout-b');},0);setTimeout(()=>{console.log('setTimeout-c');},0);setTimeout(()=>{console.log('setTimeout-d');},10000);console.log(1);console.log(2);console.log(3);console.log(4);console.log(5);for(leti=0;i<10000222200;i++){}//setTimeout回调执行完才会执行。从上面的运行结果可以看出,虽然setTimeout放在了主逻辑的最前面,但还是要等到主逻辑执行完代码后才会执行。由于第29行循环逻辑较多,主要逻辑会阻塞20秒左右,所以当调用第19行setTimeout的回调函数时,会忽略设置的timeout参数,立即执行回调函数。下面通过一段Java代码来演示多线程,说明单线程和多线程的区别:Java多线程代码运行机制:publicclassMain{publicstaticvoidmain(String[]args){//console打印输出System.out.println("1");//启动一个线程newThread(newRunnable(){@Overridepublicvoidrun(){System.out.println("Thread1before");for(inti=0;i<2099222220L;i++){}System.out.println("Thread1after");}}).start();//控制台打印输出System.out.println("2");//启动一个线程newThread(newRunnable(){@Overridepublicvoidrun(){System.out.println("Thread2");}}).start();//控制台打印输出System.out.println("3");//通过一个大循环阻塞主线程一段时间,看是否会影响线程的运行for(inti=0;i<2099222220L;i++){}//consoleprintOutputSystem.out.println("4");}}从运行结果可以看出,在多线程语言中,主线程和子线程是完全独立的,即使主线程有大量的计算逻辑,也不会阻塞子线程的运行;子线程也是相互独立的。例如:线程1存在大量的计算逻辑,不会影响线程2的执行。Java默认的子线程执行顺序是由cpu随机调度的。上述线程1启动后,cpu并没有立即对其进行调度。process.nextTick()process.nextTick()是Node.js提供的异步执行函数,不是setTimeout(fn,0)的别名,效率更高,执行顺序早于setTimeout和setInterval,它在主逻辑结束的任务队列调用之前执行。下面通过一段代码验证:/***执行顺序:主线程逻辑=>nextTick=>setTimeout**/console.log(1);setTimeout(()=>console.log('setTimeout=>1'),0);process.nextTick(()=>console.log('nextTick=>1'));console.log(2);setTimeout(()=>console.log('setTimeout=>2'),0);process.nextTick(()=>{console.log('nextTick=>2');for(leti=0;i<10000222200;i++){}//等到执行完毕任务队列中的下一个nextTick()和回调函数});console.log(3);process.nextTick(()=>console.log('nextTick=>3'));setTimeout(()=>console.log('setTimeout=>3'),0);console.log(4);setTimeout(()=>console.log('setTimeout=>4'),0);process.nextTick(()=>console.log('nextTick=>4'));console.log(5);for(leti=0;i<10000222200;i++){}//等待它完成,然后再执行nextTick和setTimeout的回调.从运行结果我们可以发现,虽然setTimeout设置早于process.nextTick(),但是process.nextTick()的执行时机还是早于setTimeout,证明是process.nextTick()的执行时机)在任务队列调用之前执行。setInterval()setInterval()是一个定时器函数,它在指定的时间段(以毫秒为单位)连续调用函数或计算表达式。但是由于JavaScript是单线程语言,所以这个定时器指定的周期回调时间是不准确的;我们通过一段代码来解释一下:/***setInterval也是等待主线程执行完再进行调用,如果超时时间相同,则按照setInterval设置的顺序执行。*如果一个setInterval回调函数中有大量的计算,线程会阻塞在这个回调函数中,直到这个回调执行完才会调用其他的setInterval。*/console.log('main=>1');setInterval(()=>{console.log('setInterval=>2before');for(leti=0;i<10022222220;i++){}//这样会阻塞一段时间,计算完成后会执行下一个setInterval回调console.log('setInterval=>2after');},1000);setInterval(()=>{console.log('setInterval=>1');},1000);console.log('main=>2');for(leti=0;i<1002222200;i++){}//这里的主要逻辑将blockforloopcalculation,setInterval只有在执行完主逻辑代码后才会被调用上面代码中同时启动了两个setInterval(),回调周期为1000毫秒,但是从运行结果,我们可以发现这两个setInterval()的回调周期时间远远超过了1000毫秒;造成这种情况的原因主要有两个:第一个是代码的第17行,这里有一个很大的循环计算,它的循环时间远远超过1000毫秒,所以这两个setInterval只能在主逻辑之后执行代码被执行。第二个是代码的第9行,在第一个setInterval中也有一个较大的循环计算,其循环时间超过了1000毫秒,所以第二个setInterval的回调函数也必须等待前面的setInterval的调用才能执行完毕后进行。由此我们可以得出一个结论:setInterval()的执行时机是在主逻辑执行完之后,它的执行顺序是根据回调周期的时间和设置的先后顺序调用的,只会调用一次setInterval回调被同时执行的函数,下一个setInterval回调函数只能在上一个setInterval回调函数执行完后执行。