广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >node事件循环中事件执行的顺序
  • 514
分享到

node事件循环中事件执行的顺序

2024-04-02 19:04:59 514人浏览 安东尼
摘要

目录事件循环浏览器环境事件循环node环境事件循环六个阶段(1) setTimeout 和 setImmediate(2) process.nextTick练习例子总结:事件循环 在

事件循环

在浏览器环境下我们的js有一套自己的事件循环,同样在node环境下也有一套类似的事件循环。

浏览器环境事件循环

首先,我们先来回顾一下在浏览器的事件循环:

总结来说:

首先会运行主线程的同步代码,每一行同步代码都会被压入执行栈,每一行异步代码会压入异步api中(如:定时器线程、ajax线程等;),在执行栈没有要执行的代码时,也就是我们当前主线程没有同步代码了,任务队列会从我们的异步任务微任务队列中取一个微任务放到我们的任务队列中进行执行,将它的回调函数进而再次放到执行栈中进行执行,当微任务队列为空时,会在宏任务中取异步任务加到任务队列,进而压入执行栈,执行回调函数,然后继续在该宏任务中查找同步、异步任务,一次循环,完成了一个事件循环(事件轮询)

浏览器环境下的例子:

例子:


        console.log("1");
        setTimeout(() => {
            console.log("setTimeout");
        }, 1);
        new Promise((res, rej) => {
            console.log("Promise");
            res('PromiseRes')
        }).then(val => {
            console.log(val);
        })
        console.log("2");

分析:
首先执行栈找到第一行的同步代码,直接扔到执行栈中执行,打印1,随后为定时器setTimeout,为异步任务,将代码放到异步对列中等待执行,随后执行promise中的代码,我们要清楚promise是同步执行,它的回调是异步执行,所有打印Promise,将res(‘PromiseRes')放到异步对列中等待执行,这个时候又遇到了同步代码,打印2,当前主线程的同步代码全部执行完毕,并且执行栈中没有要执行的同步代码,这个时候webapi会从异步队列中去微任务队列中的第一个,加入到事件队列执行,将返回的回调函数压入到执行栈中执行,打印PromiseRes,随后微任务执行完毕,已经没有微任务,现在就需要从宏任务队列中取宏任务定时器,加入到任务队列中,将回调函数压入到执行栈中执行,打印setTimeout。

node环境事件循环

在node中事件循环主要分为六个阶段来实现:

外部数据输入–》轮询阶段–》检查阶段–》关闭事件回调阶段–》定时器阶段–》I/O回调阶段–》闲置阶段–》轮询阶段》…开始循环

六个阶段

图片来自网络

在这里插入图片描述

  • timers阶段:用来执行timer(setTimeout,setInterval)的回调;
  • I/O callbacks阶段:处理一些上一轮循环中少数未执行的I/O回调
  • idle,prepare 阶段:仅node内部使用,我们用不到;
  • poll阶段:获取新的I/O时间,适当的条件下node将阻塞在这里;
  • check阶段:执行setImmediate()的回调;
  • close callbacks 阶段:执行Socket的close时间回调

主要阶段
timer:
timers阶段会执行setTimeout和setInterval回调,并且是由poll阶段控制的。
同样,在node中定时器指定的时间也不是准确时间,只能是尽快执行。
poll:
poll这一阶段中,系统会做两件事情:
1.回到timer阶段执行回调
2.执行I/O回调
并且在进入该阶段时如果没有设定了timer 的话,会发生以下两件事情

如果 poll 队列不为空,会遍历回调队列并同步执行,直到队列为空或者达到系统限制
如果 poll 队列为空时,会有两件事发生
1、如果有 setImmediate 回调需要执行,poll 阶段会停止并且进入到 check 阶段执行回调
2、如果没有 setImmediate 回调需要执行,会等待回调被加入到队列中并立即执行回调,这里同样会有个超时时间设置防止一直等待下去
当然设定了 timer 的话且 poll 队列为空,则会判断是否有 timer 超时,如果有的话会回到 timer 阶段执行回调。

check阶段
setImmediate()的回调会被加入 check 队列中,从 event loop 的阶段图可以知道,check 阶段的执行顺序在 poll 阶段之后,在进入check阶段执勤poll会检查有的话到check阶段,没有的换直接到timer阶段。

(1) setTimeout 和 setImmediate

二者非常相似,区别主要在于调用时机不同。

setImmediate 设计在 poll 阶段完成时执行,即 check 阶段,只有在check阶段才会执行;
setTimeout 设计在 poll 阶段为空闲时,且设定时间到达后执行,但它在 timer 阶段执行,表示当前线程没有其他可执行的同步任务,才会在timer阶段执行定时器。

这两个执行的时机可前可后:
例子1:


// //异步任务中的宏任务
setTimeout(() => {
    console.log('===setTimeout===');
},0);
setImmediate(() => {
    console.log('===setImmediate===')
})

在这里插入图片描述

多次重复执行的结果会不同,有一种随机的感觉,出现这种情况的原因主要和setTimeout的实现代码有关,当我们不传时间参数或者设置为0的时候,nodejs会取值为1,即1ms(在浏览器端可能取值会更大一下,不同浏览器也各不相同),所以在电脑cpu性能够强,能够在1ms内执行到timers phase的情况下,由于时间延迟不满足回调不会被执行,于是只能等到第二轮再执行,这样setInterval就会先执行。
可能由于cpu多次执行相同任务用时会有细微差别,而且在1ms上下浮动,才会造成上面的随机现象
一般情况下setTimeout为0时候会在setImmediate之前执行

例子2:
当我们传入的值大于定时器timer执行的回调时间的时候会直接导致定时器在下一次事件循环中执行


setTimeout(() => {
    console.log('===setTimeout===');
},10);
setImmediate(() => {
    console.log('===setImmediate===')
})

在这里插入图片描述

例子3:
当我们将上述代码放入一个i/o中就会固定先check再而timer:


const fs = require('fs');

fs.readFile("./any.js", (data) => {
    setTimeout(() => {
        console.log('===setTimeout===');
    },10);
    setImmediate(() => {
        console.log('===setImmediate===')
    })
});

在这里插入图片描述

在第一轮循环中读取文件,在回调中,会进入check阶段进而执行setImmediate,随后timer阶段执行定时器。
setimmediate 与 settimeout 放入一个 I/O 循环内调用,则 setImmediate 总是被优先调用

(2) process.nextTick

这个函数其实是独立于 Event Loop 之外的,它有一个自己的队列,当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行。

例子1:


setTimeout(() => {
 console.log('timer1')
 Promise.resolve().then(function() {
   console.log('promise1')
 })
}, 0)
process.nextTick(() => {
 console.log('nextTick')
 process.nextTick(() => {
   console.log('nextTick')
   process.nextTick(() => {
     console.log('nextTick')
     process.nextTick(() => {
       console.log('nextTick')
     })
   })
 })
})
// nextTick=>nextTick=>nextTick=>nextTick=>timer1=>promise1

例子2:


const fs = require('fs');

fs.readFile("./any.js", (data) => {
    process.nextTick(()=>console.log('process===2'))
    setTimeout(() => {
        console.log('===setTimeout===');
    },10);
    setImmediate(() => {
        console.log('===setImmediate===')
    })
});
process.nextTick(()=>console.log('process===1'))

在这里插入图片描述

练习例子


async function async1() {
    console.log('2')
    //会等待await执行完 但是不会向下执行 因为下面输入微任务
    await async2()
    console.log('9')
  }
   
   function async2() {
    console.log('3')
  }
   
  console.log('1')
   
  setTimeout(function () {
    console.log('11')
  }, 0)
   
  setTimeout(function () {
    console.log('13')
  }, 300)
   
  setImmediate(() => console.log('12'));
   
  process.nextTick(() => console.log('7'));
   
  async1();
   
  process.nextTick(() => console.log('8'));
   
  new Promise(function (resolve) {
    console.log('4')
    resolve();
    console.log('5')
  }).then(function () {
    console.log('10')
  })
   
  console.log('6')

分析:
上面的循序就是序号的顺序;
首先打印1:
前面都是两个函数声明,所有直接打印1,这行同步代码;
打印2:
打印完1后,都是异步代码,加入异步任务队列,直接到async1函数调用,在这个函数中打印2;
打印3:
async1这个函数是个async await函数,所有也是一个变相的同步操纵等待async2函数执行,async2执行后并不会直接打印9,原因await接受的是一个promise的then操作,所以后面属于一个promise的回调操作属于微任务,加入微任务队列;
打印4:
process.nextTick为微任务,所以会继续执行promise,打印4;
打印5:
resolve()的回调不会立即执行属于微任务,加入微任务队列,所以打印5;
打印6:
最后一个主线程的同步代码,打印6;
打印7、8:
process.nextTick优先级高于其他定时器,所以会直接执行回调函数打印7、8;
打印9、10:
这个时候需要执行微任务队列中的微任务,目前有两个9和10,按照先后循序,先打印9后打印10;
打印11、12:
setTimeout为0秒比setImmediate执行早,按照先后循序,先打印11后打印12;
打印13:
setTimeout为300ms的函数,打印13;

例子:


async function async1() {
    console.log('2')
    //会等待await执行完 但是不会向下执行 因为下面输入微任务
    await async2()
    console.log('9')
  }
   
   function async2() {
    console.log('3')
  }
   
  console.log('1')
   
  setTimeout(function () {
    console.log('11')
    setTimeout(() => {
        console.log('11-1');
    },100);
    setImmediate(() => {
        console.log('11-2')
    })
  }, 0)
   
  setTimeout(function () {
    console.log('13')
    setTimeout(() => {
        console.log('15');
    },10);
    setImmediate(() => {
        console.log('14')
    })
  }, 300)
  setImmediate(() => console.log('12'));
  process.nextTick(() => console.log('7'));
  async1();
   
  process.nextTick(() => console.log('8'));
   
  new Promise(function (resolve) {
    console.log('4')
    resolve();
    console.log('5')
  }).then(function () {
    console.log('10')
  })
   
  console.log('6')

总结:

到此这篇关于node事件循环中事件执行的顺序的文章就介绍到这了,更多相关node 事件执行的顺序内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

参考:https://www.cnblogs.com/everlose/p/12846375.html

--结束END--

本文标题: node事件循环中事件执行的顺序

本文链接: https://www.lsjlt.com/news/133972.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • node事件循环中事件执行的顺序
    目录事件循环浏览器环境事件循环node环境事件循环六个阶段(1) setTimeout 和 setImmediate(2) process.nextTick练习例子总结:事件循环 在...
    99+
    2022-11-12
  • Node的事件循环是什么
    这篇文章主要介绍“Node的事件循环是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Node的事件循环是什么”文章能帮助大家解决问题。一、什么是事件循环一句话:事件循环是Nodejs处理异步操作...
    99+
    2023-07-05
  • Node中如何实现事件循环
    这篇文章主要介绍Node中如何实现事件循环,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完! Node.js是单线程的语言,是通过事件循环处理非阻塞I/O操作...
    99+
    2022-10-19
  • 一起聊聊Node中的事件循环
    事件循环是 Node.js 的基本组成部分,通过确保主线程不被阻塞来实现异步编程,了解事件循环对构建高效应用程序至关重要。下面本篇文章就来带大家深入了解Node中的事件循环 ,希望对大家有所帮助!你已经使用 Node.js 一段时间了,构建...
    99+
    2023-05-14
    Node.js 前端 JavaScript
  • 全面了解Node事件循环
    目录Node事件循环事件循环图主线程事件循环 圈timers队列的工作原理poll队列的运作方式举例梳理事件流程check 阶段setImmediate() 与 setTimeout...
    99+
    2022-11-12
  • JS的事件循环执行机制详解
    目录前言JS语言的特点JS中同步和异步的使用事件循环是什么?事件循环执行过程微任务和宏任务的区别JS执行/运行机制最后前言 在前端开发中,涉及到JS原生的使用原理是非常重要的知识点,...
    99+
    2023-05-19
    JS事件循环执行机制 JS事件循环 JS事件
  • Node中的事件循环、process.nextTick()实例分析
    这篇文章主要讲解了“Node中的事件循环、process.nextTick()实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Node中的事件循环、p...
    99+
    2022-10-19
  • 一文搞懂Node的的事件循环
    本篇文章聊聊Nodejs中的事件循环,希望带大家搞懂Nodejs中的事件循环,从此再也不怕面试官的灵魂发问:谈一下Nodejs的事件循环!想必大家面试的时候,都会被面试官问道:“谈谈Nodejs的事件循环吧”。因为本人也被问道过,但每一次都...
    99+
    2023-05-14
    后端 Node.js
  • Node事件循环机制是什么
    这篇文章主要介绍“Node事件循环机制是什么”,在日常操作中,相信很多人在Node事件循环机制是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Node事件循环机制是什么”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-07-05
  • Node事件循环的流程是什么
    这篇文章主要讲解了“Node事件循环的流程是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Node事件循环的流程是什么”吧!我们都知道目前我们用的应用程...
    99+
    2022-10-19
  • 分析node事件循环和消息队列
    目录什么是异步?为什么需要异步呢?什么是异步IO?什么是事件循环?V8引擎当js运行时消息队列什么是异步? 异步和同步应该是经常谈的一个话题了。同步的概念很简单,自上而下依次执行,必...
    99+
    2022-11-12
  • 深入了解Node事件循环(EventLoop)机制
    主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。下面本篇文章就来带大家掌握Node.js中的eventloop,希望对大家有所帮助!虽然js可以在浏览器...
    99+
    2023-05-14
    javascript Node.js 面试 前端
  • Node异步和事件循环的深入讲解
    目录前言为什么要异步?如何实现异步?基于事件循环的异步编程模型timerspendingidle、preparepollcheckclose一些注意事项总结参考资料前言 Node 最...
    99+
    2022-11-13
  • Node异步和事件循环的底层实现和执行机制实例分析
    这篇文章主要讲解了“Node异步和事件循环的底层实现和执行机制实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Node异步和事件循环的底层实现和执行机...
    99+
    2022-10-19
  • Node事件循环中的微任务队列是什么
    这篇文章主要介绍“Node事件循环中的微任务队列是什么”,在日常操作中,相信很多人在Node事件循环中的微任务队列是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Node事件循环中的微任务队列是什么”的疑...
    99+
    2023-07-06
  • PHP中for循环的执行顺序是什么
    本教程操作环境:windows7系统、PHP7.1版,DELL G3电脑for 循环是 PHP 中最复杂的循环结构。for 循环语句能够按照已知的循环次数进行循环操作,适用于明确知道执行次数的情况。for 循环的语法格式如下:for (初始...
    99+
    2016-02-26
    PHP for循环
  • nodejs中事件和事件循环的示例分析
    这篇文章主要介绍nodejs中事件和事件循环的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!nodejs中的事件循环虽然nodejs是单线程的,但是nodejs可以将操作委托给系统内核,系统内核在后台处理这些...
    99+
    2023-06-14
  • 深入浅析Node事件循环中的微任务队列
    让我们继续进行第二个实验。实验二代码// index.js Promise.resolve().then(() => console.log("this is Promise.resolve 1")); proce...
    99+
    2023-05-14
    JavaScript Node.js 前端
  • java中for循环执行的顺序图文详析
    for循环基础 for循环是最灵活也是最常用的循环结构,表达式一般如下:   for(条件表达式1;条件表达式2;条件表达式3){     语句块;   } 接下来详细介绍J...
    99+
    2022-11-12
  • Node.js中事件循环的方法
    本文小编为大家详细介绍“Node.js中事件循环的方法”,内容详细,步骤清晰,细节处理妥当,希望这篇“Node.js中事件循环的方法”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Node 自身的执行模型——事件循...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作