一文了解Nodejs中的模块化和事件循环

2023-05-16

本篇文章带大家了解一下Nodejs中的模块化和事件循环。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

5.20出了一款线上Ide,能够在浏览器上边运行 Node.js —WebContainers

1 Node.js简介

Node.js 到底是什么?开始学习的时候,对于前端的一些知识领域没有太多的接触(当然现在也一样),对于 Node.js 的印象就是,它和Javascript 的语法几乎一样,然后是写后端的。记得当时还窃喜,学了 Javascript = 啥都会了!好了切入正题

以前 Javascript 都是运行在浏览器上边的,Javascript 是一种高级语言,计算机不能直接读懂,毕竟二进制的计算机的世界里边就只有010101...,在这个时候浏览器中的 JavaScript 引擎,就充当了翻译官的角色,把 JavaScript 想要做什么手把手翻译给计算机,这其中的编译过程就不细说(我暂时也说不清楚)。

Node.js 基于 Chrome 浏览器的 V8 引擎,可以高效的编译 Javascript,所以可以说 Node.js 是除浏览器以外的另一个 Javascript 运行环境。

记得在腾讯云的云函数上折腾过微信公众号的简单的自动回复,当时对前端代码的模块化有了小小的体会,Node.js 的功劳!

2 初体验

server.js 文件如下

// 引入 http 模块
var http = require("http");

//  用 http 模块创建服务
 //req 获取 url 信息 (request)
 //res 浏览器返回响应信息 (response)
http.createServer(function (req, res) {
  // 设置 HTTP 头部,状态码是 200,文件类型是 html,字符集是 utf8
  //Content-Type字段用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件,不写就可能会出现乱码哦
  res.writeHead(200, {
    "Content-Type": "text/html;charset=UTF-8"
  });

  // 往页面打印值
  res.write('小林别闹');

  // 结束响应
  res.end();

}).listen(3000); // 监听3000端口

安装了 Node 的前提下在终端运行 node server.js 打开浏览器,在地址栏输入http://localhost:3000/ 就能看到页面打印出来:

1.png

此时我们在本地搭建起一个最简单的服务器,浏览器作为客户端进行访问

2.1 模块化

在上边的代码中,我们注意到了有 var http = require("http"); 这样的语句,作用是引入 http 模块. 在 Node 中,模块分为两类:一是 Node 提供的模块,称为核心模块;二是用户编写的模块,称为文件模块.http 就是核心模块之一,例如使用 http 模块可以创建服务,path 模块处理文件路径,url 模块用于处理与解析 URL.fs模块用于对系统文件及目录进行读写操作等.

2.1.1CommonJS

提到模块化,就必须提一嘴 CommonJS,Node.js 就采用了部分 CommonJS 语法,可以理解为 CommonJS 是一种模块化的标准.在早期为了解决通过script标签引入js文件代码产生的依赖顺序易出错,顶层作用域导致的变量污染等问题

在这里可以梳理一下导出 module.exports 和 exports 的差别

test2.js 如下:

let str = require('./test1');
console.log(str)

当 test1.js如下:

let str1 = '小林别闹1'
let str2 = '小林别闹2'

exports.str1 = str1
exports.str2 = str2
console.log(module.exports === exports)

在终端执行 node test2.js 结果如下:

/*输出
{ str1: '小林别闹1', str2: '小林别闹2' }
true
*/

//改变test1.js文件变量暴露的方式
/*
exports.str1 = str1
module.exports = str2
console.log(module.exports === exports)
输出:
false
小林别闹2
*/

/*
exports.str1 = str1
module.exports.str2 = str2
console.log(module.exports === exports)
控制台输出:
true
{ str1: '小林别闹1', str2: '小林别闹2' }
*/

可以进行一下总结:

在 Node 执行一个文件时,会给这个文件内生成一个 exports 对象和一个 module 对象,而这个module 对象又有一个属性叫做 exports ,exports 是对 module.exports 的引用,它们指向同一块地址,但是最终导出的是 module.exports,第二次测试 module.exports = str2 改变了地址,所以 str1 没有导出.

另外注意,使用 exports 导出是导出一个对象

2.1.2 Es Module

Javascript 也是在不断的发展进步,这不,Es6版本就加入了Es Module模块

导出:

export const str1 = '小林别闹1'
export const str2 = '小林别闹2'
export default {
    fn() {},
    msg: "小林别闹"
}

导入:

import { st1,str2,obj } from './test.js'

注意 import,直接 node js 文件执行会报错的,需要 babel 编译

比较一下的话就是:

CommonJs 可以动态加载语句,代码发生在运行时

Es Module 是静态的,不可以动态加载语句,只能声明在该文件的最顶部,代码发生在编译时

2.1.3 第三方模块

在Node 中除了可以使用自己提供的核心模块,自定义模块,还可以使用第三方模块

这就需要提到 npm ,npm 是 Node 的包管理工具,已经成为了世界上最大的开放源代码的生态系统,我们可以下载各种包.

当然包管理工具还有yarn,但是我暂时只用过 npm,因为它随 node 一起按照提供.

2.2  Node 的事件循环

2.2.1 非阻塞I/O

Java、PHP 或者 .NET 等服务端语言,会为每一个客户端的连接创建一个新的线程。Node 不会为每一个客户连接创建一个新的线程,而仅仅使用一个线程。

console.log('1')
setTimeout(() => {
  console.log('2')
})
console.log('3')//输出132

Javascript 的代码是从上到下一行行执行的,但是这里就不会阻塞,输出3,再输出2

2.2.2事件循环

Node 的事件循环真的好久才弄懂一丢丢,看过很多博客,觉得理解 Node 的事件循环机制,结合代码及其运行结果来分析是最容易理解的。

libuv 库负责 Node API 的执行。它将不同的任务分配给不同的线程,形成一个 Event Loop(事件循环),以异步的方式将任务的执行结果返回给 V8 引擎。其中 libuv 引擎中的事件循环分为 6 个阶段,它们会按照顺序反复运行。每当进入某一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段。

console.log('start')
setTimeout(() => {//定时器1
  console.log('timer1')
  setTimeout(function timeout () {//定时器2
    console.log('timeout');
  },0);
  setImmediate(function immediate () {//定时器3
    console.log('immediate');
  });
  Promise.resolve().then(function() {
    console.log('promise1')
  })
}, 0)
setTimeout(() => {//定时器4
  console.log('timer2')
  Promise.resolve().then(function() {
    console.log('promise2')
  })
}, 0)
Promise.resolve().then(function() {
  console.log('promise3')
})
console.log('end')

可以 Node 上边运行一下

2.png

timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调

I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调

idle, prepare 阶段:仅node内部使用

poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里

check 阶段:执行 setImmediate() 的回调

close callbacks 阶段:执行 socket 的 close 事件回调

理解:首先执行同步任务,所以会输出start end,poll阶段是事件循环的入口,有异步事件就是从这里进入的,同步任务执行完执行先微任务,输出 promise3,接下来就是 setTimeout了,由 poll阶段一步步到 timers 阶段,执行定时器1,输出 timer1,将定时器2和定时器3加入到队列里边,一旦执行一个阶段里的一个任务就立刻执行微任务队列,所以再输出 promise1,然后执行定时器4,如上输出timer2,promise2,结合事件再循环,到了 check 阶段,执行 setImmediate() 的回调,输出 immediate,再循环进行,到达 timer 阶段,输出 timeout

3.png

2.2.3浏览器的事件循环

浏览器和 Node 的事件循环是不一样的

打算用两张图和一段代码来解释浏览器的事件循环机制,

console.log(1)
setTimeout(()=>{console.log(2)},1000)//宏任务1
async function fn(){
    console.log(3)
    setTimeout(()=>{console.log(4)},20)//宏任务2
    //return Promise.reject()返回失败状态的,不会输出6,弄不清楚为啥
    return Promise.resolve()
}
async function run(){
    console.log(5)
    await fn()
    //console.log(6),
}
run()
//需要执行150ms左右
for(let i=0;i<90000000;i++){}
setTimeout(()=>{//宏任务3
    console.log(7)
    new Promise(resolve=>{
        console.log(8)
        resolve()
    }).then(()=>{console.log(9)})
},0)
console.log(10)
// 1 5 3 10 4 7 8 9 2

执行结果如(请忽略我的工具提示):

4.png

我们可以储备一些前置知识:JavaScript 是单线程的,任务可以分为同步任务和异步任务,像 console.log('1') 就是同步的,定时器 setTimeout,promise 的回调等就是异步的。同步的很好理解,就从上到下一行一行的执行下来,异步的就有点小复杂了,还会分为宏任务和微任务。

浏览器的事件循环机制就是:先执行同步任务,同步任务执行完成,就执行任务队列里面的任务,那任务队列里面的任务是哪来的呢?异步任务准备好了就会放进任务队列,你可以理解为,在任务队列里边宏任务和微任务都存在这一个队列结构管着它们。先后的话,同步任务执行完成后,任务队列里有微任务,则将微任务执行完,再执行一个宏任务,执行了宏任务可能又产生了微任务,这是就需要再执行完微任务任务。你可以将同步任务看成宏任务,这样就可以理解为,每执行完一个宏任务都要清理一遍微任务。

5.png

6.png

上边代码解释如下:执行到第一行代码,输出 1,执行到第二行代码 setTimeout 属于宏任务1,准备1000毫秒后加入任务队列,然后执行函数 run,输出 5,因为 await 的存在,我们需要等待 fn函数执行完毕,这里是通过 await 关键字将异步函数变成同步的,执行 fn 时输出 3,又出现一个 setTimeout 宏任务2,准备时间20毫秒,返回成功状态的Promise,输出 6,for 循环需要150ms,这是宏任务2,准备完毕,进入任务队列,继续向下,有一个 setTimeout 宏任务3,无需准备加入任务队列,执行最后一行代码,输出 10,至此同步任务全部执行完毕,接下来是异步任务了,任务队列是队列的数据结构,遵循先进先出的原则,此时任务队列中有宏任务2和宏任务3,先执行宏任务2,输出 4,再执行宏任务3,输出 7promise本身是同步的,输出 8,回调then 里边的代码是微任务,宏任务3执行后,发现有微任务存在,清理一边微任务,输出 9,整个流程经过1000毫秒后,宏任务1加入任务队列,输出 2

这里直接看一位大哥的博客可能更加容易理解,写不了那么好,哭,链接放在下面了。

写的不好,羞愧万分,会努力学习的!!!

本文转载自:https://www.php.cn/js-tutorial-477187.html

更多编程相关知识,请访问:编程视频!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文了解Nodejs中的模块化和事件循环 的相关文章

随机推荐

  • 理解对象原型和原型链

    本篇文章带大家介绍一下JavaScript中的对象原型和原型链 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 对象原型 相信大家都这样用过 map xff1a let arr 61 0 1 2 le
  • JavaScript中处理异步的几种方式

    在网站开发中 xff0c 异步事件是项目必然需要处理的一个环节 xff0c 也因为前端框架的兴起 xff0c 通过框架实现的 SPA 已经是快速建构网站的标配了 xff0c 一部获取数据也就成了不可或缺的一环 xff1b 本文来就讲一讲 J
  • 软件测试 | 测试开发 | 一种基于目标检测实现黑花屏分类任务的方案

    背景 视频帧的黑 花屏的检测是视频质量检测中比较重要的一部分 xff0c 传统做法是由测试人员通过肉眼来判断视频中是否有黑 花屏的现象 xff0c 这种方式不仅耗费人力且效率较低 为了进一步节省人力 提高效率 xff0c 一种自动的检测方法
  • VSCode使用Git进行版本控制

    相关推荐 xff1a vscode基础教程 Visual Studio Code 使用Git进行版本控制 本来认为此类教程 xff0c 肯定是满网飞了 今天首次使用VS Code的Git功能 xff0c 翻遍了 所有中文教程 xff0c 竟
  • 使用Llama Logs实时可视化Node错误

    本篇文章给大家介绍一下Node开发神器 Llama Logs xff0c 使用Llama Logs实时可视化Node错误 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 相关推荐 xff1a node
  • JS中的工厂函数和构造函数

    当谈到JavaScript语言与其他编程语言相比时 xff0c 你可能会听到一些令人困惑东西 xff0c 其中之一是工厂函数和构造函数 工厂函数 所谓工厂函数 xff0c 就是指这些内建函数都是类对象 xff0c 当你调用他们时 xff0c
  • 如何使用 map 代替纯 JavaScript 对象?

    JavaScript 普通对象 key 39 value 39 可用于保存结构化数据 但是我发现很烦人的一件事 xff1a 对象的键必须是字符串 xff08 或很少使用的符号 xff09 如果用数字作键会怎样 xff1f 在这种情况下没有错
  • 深入浅析Service Workers

    本篇文章给大家介绍一下JavaScript API Service Workers 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 相关推荐 xff1a 编程入门 service worker 是什么
  • javascript中的modules、import和export

    在互联网的洪荒时代 xff0c 网站主要用 HTML和 CSS 开发的 如果将 JavaScript 加载到页面中 xff0c 通常是以小片段的形式提供效果和交互 xff0c 一般会把所有的 JavaScript 代码全都写在一个文件中 x
  • 分享Atom入坑需要安装的一些插件

    本篇文章给大家推荐一些Atom入坑必备插件 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 相关推荐 xff1a atom使用教程 Atom作为Javascript CSS HTML等前端编辑器利器
  • angular中使用jsencrypt插件

    本篇文章给大家介绍一下angular中jsencrypt插件的使用方法 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 相关推荐 xff1a angular教程 angular使用jsencrypt插
  • 使用 Node 处理 I/O 密集型任务

    下面本篇文章给大家介绍一下使用 nodejs 多线程 处理高并发任务的方法 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 相关推荐 xff1a nodejs视频教程 摩尔定律 摩尔定律是由英特尔联合
  • 软件测试 | 测试开发 | Android App 保活服务的配置与禁用

    Android应用保活是应用 系统 用户三个角色相互影响的产物 几乎每一款应用都希望自己能实现永久保活 xff0c 并通过保活实现消息推送 信息上报等等交互行为 xff1b 几乎所有的系统厂商都想把应用关在笼子里 xff0c 通过控制应用的
  • JavaScript中拷贝数组的14个小技巧

    相关推荐 xff1a javascript视频教程 数组拷贝经常被误解 xff0c 但这并不是因为拷贝过程本身 xff0c 而是因为缺乏对 JS 如何处理数组及其元素的理解 JS 中的数组是可变的 xff0c 这说明在创建数组之后还可以修改
  • JavaScript中的内存管理

    相关推荐 xff1a javascript视频教程 大多数时候 xff0c 我们在不了解有关内存管理的知识下也只开发 xff0c 因为 JS 引擎会为我们处理这个问题 不过 xff0c 有时候我们会遇到内存泄漏之类的问题 xff0c 这个只
  • 详解bootstrap自定义侧边导航栏的方法

    本篇文章给大家介绍一下bootstrap自定义侧边导航栏的方法 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 bootstrap自带的响应式导航栏是向下滑动的 xff0c 有时满足不了个性化的需求
  • 使用VSCode调试Golang工程

    本篇文章给大家介绍一下使用VSCode调试Golang工程的方法 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 推荐学习 xff1a vscode教程 关键字 最简单的调试攻略多项目调试 适用个人开
  • 深入解析JavaScript中的作用域

    本篇文章带大家深入理解JavaScript作用域 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 这篇文章称为笔记更为合适一些 xff0c 内容来源于 你不知道的JavaScript xff08 上卷
  • 12个提升程序员软技能与效率的开发工具(转载)

    本篇文章给大家推荐12个提升程序员软技能与效率的开发工具 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 大家好 xff0c 我是你们的 猫哥 xff0c 那个不喜欢吃鱼 又不喜欢喵 的超级猫 这一期
  • 一文了解Nodejs中的模块化和事件循环

    本篇文章带大家了解一下Nodejs中的模块化和事件循环 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 5 20出了一款线上Ide xff0c 能够在浏览器上边运行 Node js WebContai