- NodeJs从零开始学习计划
- NodeJs的介绍
- JavaScript 的基础知识
- NodeJs的安装及运行机制
- NodeJs常用模块
- NodeJs的文件读写及Http服务器的创建
- 回调函数及异步事件
- NodeJs的模块系统
- Web 开发基础
- NodeJs数据库交互
- NodeJs相关测试框架
- NodeJs安全
- NodeJs性能优化
- Node.js 中使用 GraphQL、WebSockets 和 Serverless
回调函数及异步事件
class 回调函数,异步事件1. 深入理解回调函数、Promise、async/await
在 Node.js 中,异步编程是核心概念。为了处理异步操作,主要使用三种技术:回调函数、Promise,以及更现代的 async/await
语法。
1.1 回调函数
回调函数是将函数作为参数传递给另一个函数,然后在适当的时候调用它。Node.js 的异步操作广泛使用回调函数。
示例:使用回调函数进行异步操作
const fs = require('fs');
// 使用回调函数读取文件
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
console.log('File content:', data);
});
在这个例子中,fs.readFile
异步读取文件内容,并通过回调函数处理读取结果。
回调地狱(Callback Hell)
当多个异步操作需要按顺序执行时,使用回调函数可能导致代码嵌套过深,难以维护。这种情况称为“回调地狱”。
示例:回调地狱
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.readFile('file3.txt', 'utf8', (err, data3) => {
if (err) return console.error(err);
console.log(data1, data2, data3);
});
});
});
1.2 Promise
Promise 是为了解决回调地狱的问题而引入的一种异步操作管理方式。它代表了一个将来会完成的操作(成功或失败)。
示例:使用 Promise 进行异步操作
const fs = require('fs').promises;
// 使用 Promise 读取文件
fs.readFile('example.txt', 'utf8')
.then(data => {
console.log('File content:', data);
})
.catch(err => {
console.error('Error reading file:', err);
});
在这个例子中,fs.readFile
返回一个 Promise 对象,我们可以通过 then
方法处理成功结果,通过 catch
方法处理错误。
链式调用(Promise Chaining)
多个异步操作可以通过链式调用 then
来按顺序执行。
示例:Promise 链式调用
fs.readFile('file1.txt', 'utf8')
.then(data1 => {
return fs.readFile('file2.txt', 'utf8');
})
.then(data2 => {
return fs.readFile('file3.txt', 'utf8');
})
.then(data3 => {
console.log(data1, data2, data3);
})
.catch(err => {
console.error('Error:', err);
});
1.3 async/await
async/await
是对 Promise 的进一步封装,使异步代码看起来像同步代码,从而提高代码的可读性。
示例:使用 async/await 进行异步操作
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(data1, data2, data3);
} catch (err) {
console.error('Error:', err);
}
}
readFiles();
在这个例子中,await
关键字使得函数暂停执行,直到 Promise 被解决,然后继续执行。这种方式非常接近同步代码的风格。
2. Node.js 中的事件驱动模型与事件循环
Node.js 是基于事件驱动架构的非阻塞 I/O 模型。理解事件驱动模型与事件循环是掌握 Node.js 的关键。
2.1 事件驱动模型
在事件驱动模型中,程序会注册一些事件的回调函数,当特定事件发生时,程序会自动触发这些回调函数。
示例:事件驱动模型
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
// 绑定事件处理函数
myEmitter.on('event', () => {
console.log('An event occurred!');
});
// 触发事件
myEmitter.emit('event');
在这个例子中,EventEmitter
对象用于管理事件的注册和触发。
2.2 事件循环
事件循环是 Node.js 处理异步操作的核心机制。事件循环不断地检查事件队列(event queue),并执行其中的回调函数。
事件循环的工作原理:
- Timers:处理
setTimeout
和setInterval
。 - I/O callbacks:处理大多数的 I/O 事件回调。
- idle, prepare:仅在系统内部使用。
- poll:检索新的 I/O 事件;执行 I/O 相关回调(几乎所有的回调都在此阶段执行)。
- check:处理
setImmediate
回调。 - close callbacks:处理
close
事件回调。
示例:事件循环与异步操作
console.log('Start');
setTimeout(() => {
console.log('Timeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise');
});
console.log('End');
在这个例子中,输出顺序为 Start
-> End
-> Promise
-> Timeout
。这是因为 Promise
的回调是在微任务队列(microtask queue)中执行,而 setTimeout
的回调是在宏任务队列(macrotask queue)中执行。
总结
深入理解回调函数、Promise、async/await 是掌握异步编程的基础。Node.js 采用事件驱动模型,并通过事件循环来高效地管理异步操作。理解这些概念后,你将能够编写高效、可维护的 Node.js 应用程序。