当前位置: 首页 > news >正文

nodeJs笔记(六)

异步编程和回调

nodejs特性之一就是异步编程 它解决了JavaScript因为单线程原因多个任务执行效率低下的问题

Node.js 采用单线程事件循环架构,这意味着:
所有用户代码在主线程运行
I/O 操作(文件、网络、数据库等)由底层线程池处理
异步模型避免阻塞主线程,实现高并发

回调函数:异步编程的基础
回调函数是 Node.js 中处理异步操作的最基本方式,它是一个在异步操作完成后被调用的函数。

回调函数的特点:
错误优先约定:回调的第一个参数通常是错误对象
异步执行:在操作完成后调用
非阻塞:不会阻塞主线程执行

// 回调函数的基本模式
function asyncOperation(param, callback) {// 模拟异步操作setTimeout(() => {const error = Math.random() > 0.8 ? new Error('操作失败') : null;const result = `处理结果: ${param}`;callback(error, result);}, 100);
}// 使用回调
asyncOperation('输入数据', (err, result) => {if (err) {console.error('发生错误:', err.message);return;}console.log('操作结果:', result);
});

Node.js 事件循环机制

   ┌───────────────────────────┐
┌─>│           timers          │ (setTimeout, setInterval)
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │     pending callbacks     │ (I/O 回调)
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │       idle, prepare       │ (内部使用)
│  └─────────────┬─────────────┘      ┌───────────────┐
│  ┌─────────────┴─────────────┐      │   incoming:   │
│  │           poll            │<─────┤  connections, │
│  └─────────────┬─────────────┘      │   data, etc.  │
│  ┌─────────────┴─────────────┐      └───────────────┘
│  │           check           │ (setImmediate)
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤      close callbacks      │ (关闭事件回调)└───────────────────────────┘

回调地狱问题与解决方案
示例:

fs.readFile('file1.txt', 'utf8', (err, data1) => {if (err) return console.error(err);fs.readFile('file2.txt', 'utf8', (err, data2) => {if (err) return console.error(err);fs.writeFile('combined.txt', data1 + data2, (err) => {if (err) return console.error(err);console.log('文件合并成功');fs.readFile('combined.txt', 'utf8', (err, combined) => {if (err) return console.error(err);console.log('合并内容:', combined);});});});
});

解决方案:

  1. 命名函数:避免嵌套
function handleFile1(err, data1) {if (err) return console.error(err);fs.readFile('file2.txt', 'utf8', handleFile2.bind(null, data1));
}function handleFile2(data1, err, data2) {if (err) return console.error(err);fs.writeFile('combined.txt', data1 + data2, handleWrite);
}// ...继续分解
  1. 使用 Promise
const fs = require('fs').promises;fs.readFile('file1.txt', 'utf8').then(data1 => fs.readFile('file2.txt', 'utf8').then(data2 => [data1, data2])).then(([data1, data2]) => fs.writeFile('combined.txt', data1 + data2)).then(() => fs.readFile('combined.txt', 'utf8')).then(combined => console.log('合并内容:', combined)).catch(err => console.error(err));
  1. 使用 async/await
async function combineFiles() {try {const data1 = await fs.promises.readFile('file1.txt', 'utf8');const data2 = await fs.promises.readFile('file2.txt', 'utf8');await fs.promises.writeFile('combined.txt', data1 + data2);const combined = await fs.promises.readFile('combined.txt', 'utf8');console.log('合并内容:', combined);} catch (err) {console.error(err);}
}

常见异步模式
4. 并行执行

const fs = require('fs');let completed = 0;
const results = [];function checkCompletion() {if (++completed === 3) {console.log('所有操作完成:', results);}
}fs.readFile('file1.txt', 'utf8', (err, data) => {if (!err) results[0] = data;checkCompletion();
});fs.readFile('file2.txt', 'utf8', (err, data) => {if (!err) results[1] = data;checkCompletion();
});fs.readFile('file3.txt', 'utf8', (err, data) => {if (!err) results[2] = data;checkCompletion();
});

使用 Promise.all 改进

const fs = require('fs').promises;Promise.all([fs.readFile('file1.txt', 'utf8'),fs.readFile('file2.txt', 'utf8'),fs.readFile('file3.txt', 'utf8')
]).then(results => {console.log('所有操作完成:', results);}).catch(err => {console.error('发生错误:', err);});

注意:
始终处理错误:避免忽略回调中的错误
避免深度嵌套:使用命名函数或 Promise 解耦
使用 async/await:对于现代 Node.js 应用
遵循错误优先模式:保持代码一致性
使用工具库:如 async 模块处理复杂流程
合理使用事件发射器:对于重复性事件

const { EventEmitter } = require('events');class FileProcessor extends EventEmitter {process(file) {fs.readFile(file, 'utf8', (err, data) => {if (err) {this.emit('error', err);return;}try {const result = this.transform(data);this.emit('processed', file, result);} catch (transformErr) {this.emit('error', transformErr);}});}transform(data) {// 数据处理逻辑return data.toUpperCase();}
}// 使用
const processor = new FileProcessor();
processor.on('processed', (file, result) => {console.log(`文件 ${file} 处理完成: ${result.slice(0, 20)}...`);
});
processor.on('error', err => console.error('处理错误:', err));processor.process('example.txt');

最佳实践

1. 始终处理错误

async function main() {try {// 业务逻辑} catch (error) {console.error('Unhandled error:', error);process.exit(1);}
}

2. 合理使用 Promise 方法

// 使用Promise.allSettled获取所有结果
async function batchProcess(items) {const results = await Promise.allSettled(items.map(processItem));const successes = results.filter(r => r.status === 'fulfilled').map(r => r.value);const errors = results.filter(r => r.status === 'rejected').map(r => r.reason);return { successes, errors };
}

3. 避免阻塞事件循环

// 将CPU密集型任务移出主线程
async function heavyComputation(data) {// 使用worker_threads或child_processreturn new Promise((resolve, reject) => {const worker = new Worker('./compute.js');worker.postMessage(data);worker.on('message', resolve);worker.on('error', reject);});
}

常见陷阱与解决方案

  1. 忘记 await
async function saveUser(user) {// 错误:忘记await,返回Promise而不是结果return db.save(user); 
}// 正确
async function saveUser(user) {await db.save(user);return user;
}
  1. 循环中的 await
// 错误:顺序执行导致性能问题
async function processArray(array) {for (const item of array) {await process(item); // 逐个等待}
}// 正确:并行处理
async function processArray(array) {await Promise.all(array.map(process));
}
  1. 错误传播中断
async function parent() {try {await child1(); // 如果出错,child2不会执行await child2();} catch (e) {// 处理错误}
}// 解决方案:独立处理
async function parent() {try {await Promise.all([child1().catch(handleError),child2().catch(handleError)]);} catch (e) {// 处理未捕获错误}
}

Node.js 特定应用

  1. 与 Streams 结合
const { pipeline } = require('stream/promises');async function processFile(input, output) {await pipeline(fs.createReadStream(input),zlib.createGzip(),fs.createWriteStream(output));console.log('Pipeline succeeded');
}
  1. 与 EventEmitter 集成
const { once } = require('events');async function waitForDatabase() {const db = new Database();try {await once(db, 'connect');console.log('Database connected');// 等待多个事件const [result] = await Promise.race([once(db, 'queryComplete'),once(db, 'error')]);return result;} catch (err) {console.error('Database error:', err);throw err;}
}

总结
Node.js 的异步编程是其高性能的核心:

回调是基础但易导致"回调地狱"

Promise 和 async/await 提供了更优雅的解决方案

理解事件循环机制对编写高效代码至关重要

结合事件发射器可构建更复杂的异步系统

现代应用应优先使用 Promise 和 async/await,同时理解回调机制对于维护遗留代码和深入理解 Node.js 运行原理仍然非常重要。

http://www.dtcms.com/a/286540.html

相关文章:

  • Ubuntu系统下交叉编译Android的X265库
  • 【leetcode】3202. 找出有效子序列的最大长度(2)
  • 联合利华发起TRO维权行动,OUAI商标或引发跨境圈大批量冻结
  • 密码学基础概念详解:从古典加密到现代密码体系
  • 函数指针和指针函数
  • 【非辐射复合】半导体中非辐射载流子复合的双能级机制
  • 储能BMS通信“卡壳”?CAN转EtherCAT让电池数据“跑起来”
  • animation-timing-function动画的速度曲线
  • 面试150 被围绕的区域
  • 数据结构——单调栈
  • PHP语法高级篇(三):过滤器与JSON
  • 计算机“十万个为什么”之跨域
  • STM32 RTOS 开发基础:从任务管理到同步机制的全面解析
  • 深入解析PyQt5信号与槽的高级玩法:解锁GUI开发新姿势
  • codesys【串口】
  • 搜索 #1 DFS讲解
  • HBase2.5.4单机模式与伪分布式的安装与配置(Ubuntu系统)
  • Python学习笔记4
  • ts学习2
  • 用AI生成了一个名叫Janitor AI导航网站
  • Android性能优化之UI渲染优化
  • 静态时序分析:门控时钟建立时间检查
  • 无人机悬停技术运行与难点分析
  • Linux 服务器中,Tab 键自动补全功能失效
  • 免费好用,闪电般快速的AI 3D模型生成器
  • 信息检索革命:Perplexica+cpolar打造你的专属智能搜索中枢
  • 写在 35 岁生日的时候
  • Web3+AI融合新纪元:Sollong用智能终端重塑协作计算未来
  • unity Physics.RaycastNonAlloc
  • 反序列化漏洞1-PHP序列化基础概念(0基础超详细)