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

相关推荐
不怕麻烦的鹿丸31 分钟前
node.js判断在线图片链接是否是webp,并将其转格式后上传
前端·javascript·node.js
阿陈陈陈36 分钟前
【Node.js入门笔记11---模块化】
笔记·node.js
海上彼尚3 小时前
使用 Node.js 从零搭建 Kafka 生产消费系统
分布式·kafka·node.js
天天扭码6 小时前
程序员防坑指南:那些年我们被JS数据类型坑过的日子
前端·javascript·node.js
zy0101016 小时前
React 开发环境搭建
前端·react.js·前端框架·npm·node.js
Jice197 小时前
Node.js 路由:构建灵活可扩展 Web 应用的关键
node.js
自求多芙8 小时前
node.js笔记
笔记·node.js
混血哲谈10 小时前
Webpack 中动态导入(Dynamic Import)的几种典型用法
前端·webpack·node.js
GISBox11 小时前
PLY格式文件如何转换成3DTiles格式——使用GISBox软件实现高效转换
arcgis·信息可视化·node.js·gis·webgl·cesium
不想说话的麋鹿11 小时前
「项目实战」从0搭建NestJS后端服务(四):Redis的集成和配置
前端·node.js