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

相关推荐
csdn_aspnet3 小时前
Node.js 使用 WebSockets 和 Socket.IO 实现实时聊天应用程序
node.js
whhhhhhhhhw7 小时前
Node.js核心API(fs篇)
node.js
聪聪的学习笔记7 小时前
【1】确认安装 Node.js 和 npm版本号
前端·npm·node.js
GDAL15 小时前
Node.js v22.5+ 官方 SQLite 模块全解析:从入门到实战
数据库·sqlite·node.js
RunsenLIu19 小时前
基于Vue.js + Node.js + MySQL实现的图书销售管理系统
vue.js·mysql·node.js
Allen_zx21 小时前
Elpis - 基于 Koa + Vue3 的企业级全栈应用框架
node.js
鹏程1 天前
局域网下五子棋,html+node.js实现
node.js·html
爱分享的程序员1 天前
前端面试专栏-算法篇:17. 排序算法
前端·javascript·node.js
盛夏绽放1 天前
接口验证机制在Token认证中的关键作用与优化实践
前端·node.js·有问必答
GDAL1 天前
Node.js REPL 教程
node.js·编辑器·vim