Node.js回调地狱

Node.js

前言:今天学习的时候看到回调地狱这个老生常谈的场景,以前都是一概而过,今天深入了解并且将其剖析了一下,总结出以下笔记:

node.js回调函数

node.js是单线程 的,但通过事件驱动回调机制实现异步操作

回调地狱

多个异步操作按顺序执行多层嵌套,导致代码的可读性和可维护性低:如

javascript 复制代码
  fs.readFile('file1.txt', 'utf8', (err, data1) => {
      if (err) {
          console.error('Error reading file1:', err);
          return;
      }
  ​
      fs.readFile('file2.txt', 'utf8', (err, data2) => {
          if (err) {
              console.error('Error reading file2:', err);
              return;
          }
  ​
          fs.readFile('file3.txt', 'utf8', (err, data3) => {
              if (err) {
                  console.error('Error reading file3:', err);
                  return;
              }
  ​
              console.log('Data from all files:', data1, data2, data3);
          });
      });
  });
解决办法:三种
promises

允许使用链式调用.then()的方式处理异步操作,避免回调地狱

javascript 复制代码
  const fs = require('fs').promises;
  ​
  fs.readFile('file1.txt', 'utf8')
      .then(data1 => {
          console.log('Data from file1:', data1);
          return fs.readFile('file2.txt', 'utf8');
      })
      .then(data2 => {
          console.log('Data from file2:', data2);
          return fs.readFile('file3.txt', 'utf8');
      })
      .then(data3 => {
          console.log('Data from file3:', data3);
      })
      .catch(err => {
          console.error('Error reading files:', err);
      });
使用async/await

async/await是ES2017引入的语法糖,可以更方便地处理异步操作,避免回调地狱

javascript 复制代码
  
  const fs = require('fs').promises;
  ​
  async function readFiles() {
      try {
          const data1 = await fs.readFile('file1.txt', 'utf8');
          const data2 = await fs.readFile('file2.txt', 'utf8');
          const data3 = await fs.readFile('file3.txt', 'utf8');
  ​
          console.log('Data from all files:', data1, data2, data3);
      } catch (err) {
          console.error('Error reading files:', err);
      }
  }
  ​
  readFiles();
事件驱动编程
首先需要明白:什么是事件驱动编程呢?

概念:事件驱动编程中,程序会等待事件的出现,一旦事件发生,就会执行预先定义好的处理函数(也称为事件监听器)。在 Node.js 里,很多对象都可以触发事件,像文件读写流、网络请求等。

事件驱动编程的工作机制

(1) 事件发射器 :Node.js 的events模块提供了EventEmitter类,这是事件驱动编程的基础。可以通过创建EventEmitter的实例来创建、触发和监听事件。

(2) 事件触发: 某个特定的操作完成或者某个条件满足时,就会触发相应的事件

(3) 事件监听 :程序可以通过on或者addListener方法来监听特定的事件,当事件触发时,与之关联的回调函数就会被执行。

以下是一个简单的实例,实现了如何进行事件驱动编程:

javascript 复制代码
  
  const EventEmitter = require('events')
  ​
  //创建一个实例对象
  const myEmitter = new EventEmitter()
  ​
  //定义事件监听器 
  const eventListener = (message) => {
      console.log(`接收到:${message}`)
  }
  ​
  //监听自定义事件 testEvent
  myEmitter.on('testEvent',eventListener)
  ​
  //触发自定义事件
  myEmitter.emit('testEvent','成功发送message触发自定义事件')
  ​
  //移除事件监听器
  myEmitter.removeListener('testEvent', eventListener);
  ​
  // 再次触发事件,此时监听器已移除,不会有输出
  myEmitter.emit('testEvent', '这是另一个自定义事件消息');

以上是事件驱动编程的工作机制,运行结果为:

事件驱动编程的优势
  • 异步处理:事件驱动编程让 Node.js 能够高效处理异步操作,避免了阻塞线程,提升了程序的性能和响应能力。
  • 高并发处理:由于可以同时监听多个事件,Node.js 能够轻松应对大量并发请求,适用于构建高性能的网络应用。
  • 代码可维护性:事件驱动的设计使得代码结构更加清晰,每个事件处理函数都可以独立开发和测试,降低了代码的耦合度(模块之间的依赖程度)。
事件驱动编程解决回调地狱

模拟多个异步操作的顺序执行的解决方法:

javascript 复制代码
  const EventEmitter = require('events');
  ​
  // 创建一个自定义事件发射器
  const myEmitter = new EventEmitter();
  ​
  // 模拟第一个异步操作
  function asyncOperation1() {
      setTimeout(() => {
          console.log('异步操作 1 完成');
          // 触发事件,表示操作 1 完成
          myEmitter.emit('operation1Completed');
      }, 1000);
  }
  ​
  // 模拟第二个异步操作
  function asyncOperation2() {
      setTimeout(() => {
          console.log('异步操作 2 完成');
          // 触发事件,表示操作 2 完成
          myEmitter.emit('operation2Completed');
      }, 1000);
  }
  ​
  // 模拟第三个异步操作
  function asyncOperation3() {
      setTimeout(() => {
          console.log('异步操作 3 完成');
          // 触发事件,表示操作 3 完成
          myEmitter.emit('operation3Completed');
      }, 1000);
  }
  ​
  // 监听操作 1 完成事件
  myEmitter.on('operation1Completed', () => {
      asyncOperation2();
  });
  ​
  // 监听操作 2 完成事件
  myEmitter.on('operation2Completed', () => {
      asyncOperation3();
  });
  ​
  // 启动第一个异步操作
  asyncOperation1();

代码种模拟了三个定时器的异步操作,通过on 监听每个操作完成的事件,当事件触发时,执行下一个异步操作。通过调用asyncOperation1 启动整个流程,代码执行能够完美运行,解决了回调地狱问题:

相关推荐
Stream_Silver20 小时前
【Node.js 安装报错解决方案:解决“A later version of Node.js is already installed”问题】
node.js
Anthony_2311 天前
基于 Vue3 + Node.js 的实时可视化监控系统实现
node.js
说给风听.1 天前
解决 Node.js 版本冲突:Windows 系统 nvm 安装与使用全指南
windows·node.js
森叶1 天前
Node.js 跨进程通信(IPC)深度进阶:从“杀人”的 kill 到真正的信号
node.js·编辑器·vim
虹科网络安全2 天前
艾体宝新闻 | NPM 生态系统陷入困境:自我传播恶意软件在大规模供应链攻击中感染了 187 个软件包
前端·npm·node.js
摇滚侠2 天前
PNPM 包管理工具和 NPM 包管理工具
vscode·npm·node.js·pnpm
心柠2 天前
webpack
前端·webpack·node.js
FreeBuf_2 天前
vm2 Node.js库曝严重沙箱逃逸漏洞(CVE-2026-22709)可导致任意代码执行
node.js
147API2 天前
改名后的24小时:npm 包抢注如何劫持开源项目供应链
前端·npm·node.js
抵梦2 天前
NPM、CNPM、PNPM:Node.js 依赖工具对比与选择
前端·npm·node.js