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 启动整个流程,代码执行能够完美运行,解决了回调地狱问题:

相关推荐
FungLeo11 小时前
成为全栈·Node 后端篇·配置管理:环境变量、多环境与密钥安全
node.js·环境变量·多环境配置·密钥安全
FungLeo12 小时前
成为全栈·Node 后端篇·错误处理:异常分层与全局捕获
node.js·错误处理·异常分层·全局捕获·成为全栈
百万运营Pro19 小时前
用 Astro + Supabase 从零构建全网盘聚合搜索引擎:PGroonga 中文检索实战
搜索引擎·前端框架·node.js·个人开发·学习方法·ai编程·资源分享
小婉1 天前
我用 Next.js + React Flow 从零搭建了一个可视化 AI 工作流编排平台
前端·人工智能·node.js
抓不住时间的沙2 天前
N1搭建守护环境以及重装 Armbian 后完整恢复整套守护环境,清理日志步骤
node.js
meilindehuzi_a2 天前
从域名到数据库:React + Node.js 项目部署全流程与用户访问链路
数据库·react.js·node.js
李游Leo2 天前
Node.js 开发环境安装与 npm/pnpm 国内镜像配置(Windows / macOS / Linux)
npm·node.js·pnpm·前端开发·开发环境
阿黎梨梨3 天前
AI也有记忆?LangChain Memory 管理指南
langchain·node.js·llm
小小龙学IT3 天前
libuv 开源异步 I/O 事件循环库深度解析:Node.js 的心脏,C++ 高性能网络程序的引擎
c++·开源·node.js
用户672465366054 天前
Node 守护进程日志转发踩坑记:stdio 管道、UTF-8 截断,和一个字符串按值传参的故事
node.js