Node.js入门教程(十一):异步编程

一、为什么需要异步编程?

在传统的同步(Synchronous)编程中,代码是按顺序执行的,每一行代码必须等待前一行代码执行完成后才能运行。而异步编程(Asynchronous)则允许代码在等待某些操作(如 I/O 操作)完成的同时继续执行其他任务,不会阻塞程序的运行。

Node.js 采用异步 I/O 模型,这使得它特别适合处理高并发的网络应用。理解异步编程是掌握 Node.js 的关键。

Node.js 为什么需要异步编程

Node.js 是单线程的,这意味着它一次只能执行一个任务。如果使用同步编程方式,当一个耗时操作(如读取大文件或网络请求)执行时,整个程序会被阻塞,无法处理其他请求。

异步编程解决了这个问题:

  • 提高应用程序的吞吐量

  • 更有效地利用系统资源

  • 提供更好的用户体验

二、Node.js 异步编程的三种主要方式

1、回调函数(Callbacks)

回调函数是 Node.js 中最基础的异步处理方式。它通过将一个函数作为参数传递给另一个函数,在操作完成后调用这个函数。

复制代码
const fs = require('fs');

// 异步读取文件
fs.readFile('example.txt', 'utf8', (err, data) => {
    if (err) {
        console.error('读取文件出错:', err);
        return;
    }
    console.log('文件内容:', data);
});

console.log('读取文件请求已发送,继续执行其他代码...');

特点:

  • 简单直接

  • 容易导致"回调地狱"(Callback Hell)

  • 错误处理不够直观

2、Promise

Promise 是 ES6 引入的异步编程解决方案,它表示一个异步操作的最终完成或失败及其结果值。

复制代码
const fs = require('fs').promises;

// 使用 Promise 读取文件
fs.readFile('example.txt', 'utf8')
    .then(data => {
        console.log('文件内容:', data);
    })
    .catch(err => {
        console.error('读取文件出错:', err);
    });

console.log('读取文件请求已发送,继续执行其他代码...');

特点:

  • 链式调用,避免回调地狱

  • 更好的错误处理机制

  • 状态不可逆(pending → fulfilled 或 rejected)

3、async/await

async/await 是 ES8 引入的语法糖,基于 Promise,使异步代码看起来像同步代码。

复制代码
const fs = require('fs').promises;

async function readFile() {
    try {
        const data = await fs.readFile('example.txt', 'utf8');
        console.log('文件内容:', data);
    } catch (err) {
        console.error('读取文件出错:', err);
    }
}

readFile();
console.log('读取文件请求已发送,继续执行其他代码...');

特点:

  • 代码更简洁,更易读

  • 错误处理使用 try/catch 结构

  • 必须用在 async 函数中

三、错误处理最佳实践

回调函数中的错误处理

复制代码
fs.readFile('example.txt', 'utf8', (err, data) => {
    if (err) {
        console.error('读取文件出错:', err);
        return; // 重要:处理错误后返回,避免执行后续代码
    }
    console.log('文件内容:', data);
});

Promise 的错误处理

复制代码
fs.readFile('example.txt', 'utf8')
    .then(data => {
        console.log('文件内容:', data);
        return someOtherAsyncOperation(data);
    })
    .then(result => {
        console.log('第二步结果:', result);
    })
    .catch(err => {
        console.error('处理过程中出错:', err);
    });

async/await 的错误处理

复制代码
async function processData() {
    try {
        const data = await fs.readFile('example.txt', 'utf8');
        const result = await someOtherAsyncOperation(data);
        console.log('最终结果:', result);
    } catch (err) {
        console.error('处理过程中出错:', err);
    }
}

processData();

四、性能考虑

避免阻塞事件循环

长时间运行的同步代码会阻塞整个应用:

复制代码
// 错误示例:同步读取大文件会阻塞事件循环
const data = fs.readFileSync('largefile.txt', 'utf8');
// 其他请求需要等待这个操作完成后才能被处理

合理使用异步

不是所有操作都需要异步,简单的计算任务使用同步方式可能更高效。

复制代码
// 简单的同步计算更适合用同步方式
const sum = a + b + c;

// I/O 密集型操作使用异步
fs.readFile('data.txt', 'utf8', callback);

控制并发量

过多的并行异步操作可能导致资源耗尽。可以使用控制并发量的库(如 p-limit)或手动控制。

使用流处理大文件

对于大文件,使用流(Stream)而不是一次性读取:

复制代码
const fs = require('fs');

// 使用流处理大文件
const stream = fs.createReadStream('largefile.txt', 'utf8');
stream.on('data', (chunk) => {
    // 处理数据块
    console.log('接收到数据块:', chunk.length);
});
stream.on('end', () => {
    console.log('文件读取完成');
});

五、三种方式对比

方式 优点 缺点 适用场景
回调函数 简单直接、兼容性好 回调地狱、错误处理不直观 简单异步操作、遗留代码
Promise 链式调用、错误处理好 语法稍复杂 复杂异步流程
async/await 代码简洁、可读性强 必须用在 async 函数中 推荐方式,现代开发首选

六、本章小结

知识点 说明
异步编程的必要性 避免阻塞事件循环,提高吞吐量
回调函数 Node.js 最基础的异步处理方式
Promise ES6 引入,解决回调地狱问题
async/await ES8 语法糖,使异步代码像同步代码
错误处理 回调用条件判断,Promise 用 catch,async/await 用 try/catch
性能建议 避免阻塞事件循环,使用流处理大文件

提示 :在现代 Node.js 开发中,建议优先使用 async/await 方式编写异步代码,它让异步逻辑更清晰、更易于维护。同时,要注意正确处理错误,避免未处理的 Promise 拒绝导致应用崩溃。

相关推荐
damoluomu6 小时前
SSG 的后台怎么补:Node.js 生态里的三条路(番外二)
javascript·css·typescript·node.js·html
花开富贵城7 小时前
使用fnm安装node.js包管理器
node.js
濮水大叔17 小时前
NestJS Dynamic Providers 与 Cabloy/Vona Provider Scene:同接口多实现的两种装配哲学
typescript·node.js·nestjs
silvamux_1 天前
Node.js 调用大模型 API 排错实录
node.js
Amumu121381 天前
NodeJs介绍
前端·node.js
孟陬1 天前
利用 TypeScript `asserts` function 语法定制一个类型缩窄的 `assertIncludes`
typescript·node.js
damoluomu1 天前
静态站点生成器怎么选:Node.js 生态里常用的五个(番外一)
javascript·css·typescript·node.js·html
濮水大叔1 天前
Cabloy/Vona 如何避开 NestJS 的循环依赖问题:懒属性注入机制源码解析
typescript·node.js·nestjs
月走乂山2 天前
DSH 桌面版用 DeepSeek 报 REQUEST_EXTENSION 的根因定位与修复
electron·node.js·插件系统·deepseek·bug排查·dsh·deepseek harness
斯晨的AI笔记2 天前
在服务器上跑 npm ci 四十分钟没装完,我打包把 node_modules 扔了过去
服务器·node.js