总结分享了解nodejs的几个关键节点

总结分享了解nodejs的几个关键节点

本文是个人在实际开发和学习中对nodejs的一些理解,现整理出来方便日后查阅,如果能给您启发将不胜荣幸。

非阻塞I/O

I/O:即 Input / Output,一个系统的输入和输出。

一个系统可以理解为一个个体,比如说一个人,你说话就是输出,你听就是输入。

阻塞 I/O 与非阻塞 I/O 的区别就在于系统接收输入再到输出期间,能不能接收其他输入

下面以两个例子来说明什么是阻塞 I/O 和非阻塞 I/O:

1、打饭

1.png

首先我们要确定一个系统的范围,在这个例子中食堂阿姨和餐厅的服务生看成是一个系统,输入就是点菜,输出就是端菜

那么在点菜和端菜之间能不能接受其他人的点菜,就可以判断是阻塞I/O还是非阻塞I/O。

对于食堂阿姨,他在点菜的时候,是不能帮其他同学点菜的,只有这个同学点完菜端菜走了之后,才能接受下一个同学的点菜,所以食堂阿姨是阻塞I/O。

对于餐厅服务员,他可以在点完菜以后,这个客人端菜之前是可以服务下一位客人的,所以服务员是非阻塞I/O。

2、做家务

2.png

在洗衣服的时候,是不需要等着洗衣机旁边的,这个时候可以去扫地和整理书桌,当整理完书桌后衣服也洗好了,这个时候去晾衣服,那么总共只需要25分钟。

洗衣服其实就是一个非阻塞I/O,在把衣服扔进洗衣机和洗完衣服期间,你是可以干其他事情的。

非阻塞I/O之所以能提升性能,是因为它可以把不必要的等待给节省掉。

理解非阻塞I/O的要点在于

确定一个进行I/O的系统边界。这非常关键,如果把系统扩大,上面餐厅的例子,如果把系统扩大到整个餐厅,那么厨师肯定是一个阻塞 I/O。在 I/O 过程中,能不能进行其他 I/O。

nodejs的非阻塞 I/O

nodejs的非阻塞 I/O 是怎么体现的呢?前面说过理解非阻塞 I/O 的一个重要点是先确定一个系统边界,node的系统边界就是主线程

如果下面的架构图按照线程的维护划分,左边虚线部分是nodejs线程,右边虚线部分是c++线程。

3.png

现在 nodejs 线程需要去查询数据库,这是一个典型的 I/O 操作,它不会等待 I/O 的结果,而且继续处理其他的操作,它会把大量的计算能力分发到其他的c++线程去计算。

等到结果出来后返回给nodejs线程,在获得结果之前nodejs 线程还能进行其他的I/O操作,所以是非阻塞的。

nodejs 线程 相当于左边部分是服务员,c++ 线程是厨师。

所以,node的非阻塞I/O是通过调用c++的worker threads来完成的。

那当 c++ 线程获取结果后怎么通知 nodejs 线程呢?答案是事件驱动

事件驱动

阻塞:I/O时进程休眠,等待I/O完成后进行下一步; 

非阻塞:I/O时函数立即返回,进程不等待I/O完成。

那怎么知道返回的结果,就需要用到事件驱动

所谓事件驱动可以理解为跟前端点击事件一样,我首先写一个点击事件,但是我不知道什么时候触发,只有触发的时候就去让主线程执行事件驱动函数。

这种模式也是一种观察者模式,就是我首先先监听这个事件,等触发时我就去执行。

那怎么实现事件驱动呢?答案是异步编程

BibiGPT-哔哔终结者 BibiGPT-哔哔终结者

B站视频总结器-一键总结 音视频内容

BibiGPT-哔哔终结者 28 查看详情 BibiGPT-哔哔终结者

异步编程

上面说过nodejs有大量的非阻塞I/O,那么非阻塞I/O的结果是需要通过回调函数来获取的,这种通过回调函数的方式,就是异步编程。比如下面的代码是通过回调函数获取结果的:

glob(__dirname+'/**/*', (err, res) => {    result = res    console.log('get result')})

回调函数格式规范

nodejs的回调函数第一个参数是error,后面的参数才是结果。为什么要这么做呢?

try {  interview(function () {       console.log('smile')  })} catch(err) {    console.log('cry', err)}function interview(callback) {    setTimeout(() => {        if(Math.random() < 0.1) {            callback('success')        } else {            throw new Error('fail')        }    }, 500)}

执行之后,没有被捕获,错误被扔到了全局,导致整个nodejs程序崩溃了。

4.png

没有被try catch捕获是因为setTimeout重新开启了事件循环,每开启一个事件循环就重新生一个调用栈context,try catch是属于上一个事件循环的调用栈的,setTimeout的回调函数执行的时候,调用栈都不一样了,在这个新的调用栈中是没有try catch,所以这个错误被扔到全局,无法捕获。具体可以参考这一篇文章异步队列进行try catch时的问题。

那么怎么办呢?把错误也作为一个参数:

function interview(callback) {    setTimeout(() => {        if(Math.random() < 0.5) {            callback('success')        } else {            callback(new Error('fail'))        }    }, 500)}interview(function (res) {    if (res instanceof Error) {        console.log('cry')        return    }     console.log('smile')})

但是这样就比较麻烦,在回调中还要判断,所以就产生一种约定成熟的规定,第一个参数是err,如果不存在表示执行成功。

function interview(callback) {    setTimeout(() => {        if(Math.random() < 0.5) {            callback(null, 'success')        } else {            callback(new Error('fail'))        }    }, 500)}interview(function (res) {    if (res) {        return    }     console.log('smile')})

异步流程控制

nodejs的回调写法,不仅会带来回调地域,还会带来异步流程控制的问题。

异步流程控制主要是指当并发的时候,怎么来处理并发的逻辑。还是上面的例子,如果你同事面试两家公司,只有当成功面试两家的时候,才可以不面试第三家,那么怎么写这个逻辑呢?需要全局顶一个一个变量count:

var count = 0interview((err) => {    if (err) {        return    }    count++    if (count >= 2) {        // 处理逻辑    }})interview((err) => {    if (err) {        return    }    count++    if (count >= 2) {        // 处理逻辑    }})

像上面这种写法就非常麻烦,且难看。所以,后来就出现了promise,async/await的写法。

promise

当前事件循环得不到的结果,但未来的事件循环会给你结果。很像一个渣男说的话。

promise不仅是一个渣男,还是一个状态机:

pendingfulfilled/resolvedrejectd

const pro = new Promise((resolve, reject) => {    setTimeout(() => {        resolve('2')    }, 200)})console.log(pro) // 打印:Promise {  }

then & .catch

resolved 状态的 promise 会调用后面的第一个 thenrejected 状态的 promise 会调用后面的第一个 catch任何一个 reject 状态且后面没有 .catch 的 promise,都会造成浏览器或者 node 环境的全局错误。uncaught 表示未捕获的错误。

5.png

执行then或者catch会返回一个新的promise,该promise最终状态根据then和catch的回调函数的执行结果决定:

如果回调函数始终是throw new Error,该promise是rejected状态如果回调函数始终是return,该promise是resolved状态但如果回调函数始终是return一个promise,该promise会和回调函数return的promise状态保持一致

function interview() {    return new Promise((resolve, reject) => {        setTimeout(() => {            if (Math.random() > 0.5) {                resolve('success')            } else {                reject(new Error('fail'))            }        })    })}var promise = interview()var promise1 = promise.then(() => {    return new Promise((resolve, reject) => {        setTimeout(() => {            resolve('accept')        }, 400)    })})

promise1的状态是由return里面的promise的状态决定的,也就是return里面的promise执行完后的状态就是promise1的状态。这样有什么好处呢?这样可以解决回调地狱的问题

var promise = interview()    .then(() => {        return interview()    })    .then(() => {        return interview()    })    .then(() => {        return interview()    })    .catch(e => {        console.log(e)    })

then如果返回的promise的状态是rejected,那么会调用后面第一个catch,后面的then就不会在调用了。记住:rejected调用后面的第一个catch,resolved调用后面的第一个then。

promise解决异步流程控制

如果promise仅仅是为了解决地狱回调,太小看promise了,promise最主要的作用是解决异步流程控制问题。下面如果要同时面试两家公司:

function interview() {    return new Promise((resolve, reject) => {        setTimeout(() => {            if (Math.random() > 0.5) {                resolve('success')            } else {                reject(new Error('fail'))            }        })    })}promise    .all([interview(), interview()])    .then(() => {        console.log('smile')    })    // 如果有一家公司rejected,就catch    .catch(() => {        console.log('cry')    })

async/await

sync/await到底是什么:

console.log(async function() {    return 4})console.log(function() {    return new Promise((resolve, reject) => {        resolve(4)    })})

打印的结果一样,也就是async/await是promse的语法糖而已。

我们知道try catch捕获错误是依赖调用栈的,只能捕获到调用栈以上的错误。但是如果使用await后能捕捉到调用栈所有函数的错误。即便这个错误是在另一个事件循环的调用栈抛出的,比如setTimeout。

改造面试代码,可以看到代码精简了很多。

try {    await interview(1)    await interview(2)    await interview(2)} catch(e => {    console.log(e)})

如果是并行任务呢?

await Promise.all([interview(1), interview(2)])

事件循环

因为nodejs的非阻塞 I/0, 所以需要利用事件驱动的方式获取 I/O 的结果,实现事件驱动拿到结果必须使用异步编程,比如回调函数。那么如何来有序的执行这些回调函数来获取结果呢?那就需要使用事件循环。

事件循环是实现 nodejs 非阻塞 I/O 功能的关键基础,非阻塞I/O和事件循环都是属于 libuv 这个c++库提供的能力。

6.png

代码演示:

const eventloop = {    queue: [],    loop() {        while(this.queue.length) {            const callback = this.queue.shift()            callback()        }        setTimeout(this.loop.bind(this), 50)    },    add(callback) {        this.queue.push(callback)    }}eventloop.loop()setTimeout(() => {    eventloop.add(() => {        console.log('1')    })}, 500)setTimeout(() => {eventloop.add(() => {console.log('2')})}, 800)

setTimeout(this.loop.bind(this), 50)保证了50ms就会去看队列中是否有回调,如果有就去执行。这样就形成了一个事件循环。

当然实际的事件要复杂的多,队列也不止一个,比如有一个文件操作对列,一个时间对列。

const eventloop = {    queue: [],    fsQueue: [],    timerQueue: [],    loop() {        while(this.queue.length) {            const callback = this.queue.shift()            callback()        }        this.fsQueue.forEach(callback => {            if (done) {                callback()            }        })        setTimeout(this.loop.bind(this), 50)    },    add(callback) {        this.queue.push(callback)    }}

总结

首先我们弄清楚了什么是非阻塞I/O,即遇到I/O立刻跳过执行后面的任务,不会等待I/O的结果。当I/O处理好了之后就会调用我们注册的事件处理函数,这就叫事件驱动。实现事件驱动就必须要用异步编程,异步编程是nodejs中最重要的环节,它从回调函数到promise,最后到async/await(使用同步的方法写异步逻辑)。

更多node相关知识,请访问:nodejs 教程!

以上就是总结分享了解nodejs的几个关键节点的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/549626.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月9日 19:21:27
下一篇 2025年11月9日 19:22:35

相关推荐

  • 配置 Tailwind CSS:初始化指南 |设置 Tailwind CSS:初始化指南

    简介 |简介意大利语:本文有意大利语和英语版本。向下滚动查看英文版本。英语:本文有意大利语和英语版本。向下滚动查看英文版本。 意大利语版 如何开始使用 tailwind css:项目完整指南 简介 在项目中配置 tailwind css 第一次似乎令人畏惧,特别是对于像我这样第一次尝试这个库的人来说…

    2025年12月24日
    000
  • 如何设置独立 CLI:在 Shopify 中使用 Tailwind CSS,而不使用 Nodejs

    依赖关系 Shopify CLI:一种命令行界面工具,可帮助您开发和管理 Shopify 主题。TailwindCSS:实用程序优先的 CSS 框架,用于快速构建自定义设计。 设置 我们使用 Tailwind 作为独立的 CLI 工具。更多信息可以参考官方指南。 注意:如果您在配备 Intel 处理…

    2025年12月24日
    000
  • 谷歌怎么下载html5_HTML5无需下载浏览器直接渲染运行页面文件【说明】

    HTML5不是需下载的软件,而是浏览器原生标准;Chrome默认支持,可通过file://协议直接打开本地.html文件,或用http-server启动本地服务器以支持完整API功能。 如果您已编写好HTML5页面文件,但误以为需要“下载HTML5”才能运行,则需明确:HTML5不是可下载的独立软件…

    2025年12月23日
    000
  • 手机编程html5工具_移动端代码编辑器推荐【指南】

    推荐五款移动端HTML5编辑器:Acode Editor支持一键预览;DroidEdit Pro可FTP/SFTP远程编辑;Codeanywhere提供云端IDE与Git集成;SpckEditor专注极简单页开发;Termux组合方案实现命令行本地服务。 如果您希望在手机上直接编写、调试和预览 HT…

    2025年12月23日
    000
  • Linux polybar状态栏,HTML+CSS加载进度实时!

    可通过 Bash 脚本在 Polybar 中模拟 HTML+CSS 风格进度条,利用 %{F#color} 标记和块字符 ▮ 动态显示已完成与未完成部分,结合实时任务进度输出格式化文本。 如果您希望在 Linux 的 Polybar 状态栏中显示一个基于 HTML+CSS 风格的加载进度条,并实时反…

    2025年12月23日
    000
  • Node.js Puppeteer爬虫的部署与自动化运行指南

    本教程详细指导如何部署基于node.js和puppeteer的网络爬虫,并实现其自动化定时执行。文章区分了前端静态网站与后端node.js脚本的部署策略,重点介绍了在windows环境下使用任务计划程序(task scheduler)进行定时任务配置的方法,同时提及了cors问题产生的原因及解决方案…

    2025年12月23日
    000
  • Linux Debian用Gulp自动化,CSS编译HTML零等待!

    首先安装Node.js与npm,再初始化项目并安装Gulp及插件,接着配置gulpfile.js定义Sass编译与浏览器同步任务,最后通过gulp命令启动自动化流程,实现CSS即时编译与HTML实时更新。 如果您希望在Linux Debian系统中通过Gulp实现自动化工作流,以达到CSS即时编译、…

    2025年12月23日
    000
  • Node.js爬虫的服务器端部署与定时任务配置指南

    本文旨在指导开发者如何部署基于%ignore_a_1%的puppeteer网络爬虫,并实现其定时自动化运行。针对本地开发环境,文章详细介绍了利用windows任务计划程序配置定时任务的步骤,确保node.js脚本能按预设时间自动执行,更新数据。同时,文章也探讨了生产环境下的部署思路,强调了客户端与服…

    2025年12月23日
    000
  • Node.js爬虫的部署、调度与静态网站数据集成指南

    本教程详细阐述如何部署和自动化运行node.js网络爬虫,实现定时数据抓取并更新静态网站内容。我们将探讨node.js脚本的服务器端运行机制、windows任务计划程序等调度工具的配置,以及爬取数据与静态html页面集成的策略,旨在帮助您构建高效且自动化的数据更新流程。 1. 理解Node.js爬虫…

    2025年12月23日
    000
  • 部署与自动化运行Node.js网络爬虫:使用Windows任务计划程序

    本文将指导您如何部署一个基于node.js和puppeteer的网络爬虫,并利用windows任务计划程序实现其每日定时自动运行。我们将深入探讨服务器端脚本与静态网页的区别,提供详细的步骤配置任务计划,并讨论在自动化过程中需要注意的关键事项,如资源管理、错误处理及跨平台部署思路。 理解Node.js…

    2025年12月23日
    000
  • 部署与调度Node.js爬虫:从本地执行到云端集成

    本教程详细指导如何部署和调度一个基于node.js的网页爬虫,使其能够每日自动运行并将其数据提供给前端应用。文章将区分客户端与服务器端javascript的运行环境,介绍本地任务调度方法,并探讨将爬虫集成到在线服务以实现数据共享的策略,同时涵盖数据持久化、cors处理及部署最佳实践。 1. 理解No…

    2025年12月23日
    000
  • nodejs如何打开html_Node.js环境中HTML文件打开方法

    如果您在Node.js环境中需要打开或提供HTML文件的访问,通常意味着您希望启动一个本地服务器来渲染并展示HTML页面。以下是实现该功能的具体方法: 一、使用内置http模块创建服务器 通过Node.js的内置http模块可以快速搭建一个简易服务器,用于读取并返回HTML文件内容给客户端浏览器。 …

    2025年12月23日
    000
  • WSL2里写HTML+CSS,Windows秒变Linux开发机!

    使用WSL2可在Windows中高效进行Linux环境下的HTML与CSS开发。首先启用WSL2并安装Ubuntu 22.04,确保运行版本为2;接着安装Visual Studio Code及Remote – WSL插件,将项目存于Linux文件系统以实现编辑同步;通过Python内置服…

    2025年12月23日
    000
  • Windows WSL2中Linux环境运行HTML+CSS开发服务器

    在WSL2中配置Linux环境进行HTML+CSS开发,首先启用WSL2并安装Ubuntu等发行版,运行wsl –install后重启并设置用户;接着更新系统包sudo apt update && sudo apt upgrade;推荐使用Python3内置服务器,在项目…

    2025年12月23日
    000
  • Linux用stylelint实时校验CSS是否符合HTML规范

    Stylelint用于校验CSS书写规范,可通过配置文件和编辑器插件在Linux中实现实时检查,并结合chokidar监听文件变化,确保CSS代码质量并与HTML协同工作。 Stylelint 是一个强大的 CSS 代码检查工具,但它主要用于校验 CSS 的书写规范(如格式、可维护性、错误预防等),…

    2025年12月23日
    000
  • Windows用Prettier同时格式化HTML和CSS代码

    答案:在Windows中使用Prettier格式化HTML和CSS需先安装Node.js,再通过npm安装Prettier,可全局或项目本地安装,推荐配合VS Code插件实现保存自动格式化,注意文件扩展名正确以确保语言识别。 在 Windows 系统中使用 Prettier 格式化 HTML 和 …

    2025年12月23日
    000
  • 如何在Atom中集成HTML预处理器Sass的详细教程

    首先安装Node.js和Dart Sass,再在Atom中安装atom-sass等插件,配置文件结构与编译规则,通过插件或命令行监听实现.scss文件自动编译为CSS,并在HTML中引入生成的CSS文件,从而提升开发效率。 要在Atom中集成Sass预处理器,让HTML开发更高效,关键在于安装合适的…

    2025年12月23日
    000
  • Linux Ubuntu用BrowserSync同步HTML与CSS多设备

    首先安装Node.js和npm,再全局安装BrowserSync,进入项目目录启动服务器并监听HTML与CSS文件,通过局域网IP在多设备访问,修改代码后页面自动同步刷新,支持CSS热替换,需确保设备同Wi-Fi且端口开放。 要在Linux Ubuntu系统中使用BrowserSync实现HTML与…

    2025年12月23日
    000
  • nodejs如何添加html_Node.js服务端HTML渲染与响应方法

    Node.js中返回HTML可通过原生HTTP模块直接发送字符串或使用模板引擎动态渲染。直接返回时需设置Content-Type为text/html并用res.end()发送HTML内容;对于动态数据,可结合EJS等模板引擎读取模板文件并渲染数据后返回;更推荐在中大型项目中使用Express框架,配…

    2025年12月23日
    000
  • termux如何运行html_Termux终端中HTML文件运行与预览方法

    推荐使用Python启动本地服务器预览HTML文件:进入项目目录后运行python -m http.server 8000,再通过手机浏览器访问http://localhost:8000即可查看页面效果。 在Termux中运行和预览HTML文件,虽然不能像桌面浏览器那样直接双击打开,但通过简单的工具…

    2025年12月23日
    000

发表回复

登录后才能评论
关注微信