JavaScript异步编程:从回调到async/await
JavaScript是一种单线程语言,但其执行模型能够在不阻塞主线程的情况下高效处理数千个并发操作。这得益于异步编程。在Node.js中,输入输出操作(文件读取、数据库查询、网络调用)占据了90%的时间,理解异步性成为关键技能。在本指南中,我们将探讨异步编程的三个演进阶段:回调、Promise和async/await,并深入探究事件循环的内部机制。
1. 回调(Callbacks):异步的基础
回调是一个作为参数传递给另一个函数,并在异步操作完成后被调用的函数。这是JavaScript中最古老、最基本的模式。在Node.js中,许多内置模块(fs、http)都使用回调。
使用回调读取文件的示例:
const fs = require('fs');
console.log('开始读取文件...');
fs.readFile('example.txt', 'utf8', (err, data) => { if (err) { console.error('错误:', err); return; } console.log('文件内容:', data);});
console.log('代码继续执行...');控制台输出将显示"代码继续执行..."会出现在文件内容之前。这就是非阻塞行为:主线程不会等待读取完成,而是继续执行。
回调的问题:回调地狱
当异步操作嵌套时,代码会变成"地狱金字塔":
fs.readFile('user.json', (err, userData) => { if (err) throw err; const user = JSON.parse(userData); db.query(`SELECT * FROM posts WHERE user_id=${user.id}`, (err, posts) => { if (err) throw err; fs.writeFile('user_posts.json', JSON.stringify(posts), (err) => { if (err) throw err; console.log('完成!'); }); });});这样的代码难以阅读、调试和维护。解决方案是Promise。
2. Promise:链式调用与错误处理
Promise是一个表示异步操作未来结果的对象。它可以处于三种状态之一:pending(等待中)、fulfilled(成功完成)或rejected(因错误被拒绝)。
创建一个简单的Promise:
const readFilePromise = (filename) => { return new Promise((resolve, reject) => { fs.readFile(filename, 'utf8', (err, data) => { if (err) { reject(err); } else { resolve(data); } }); });};
readFilePromise('example.txt') .then(data => console.log('数据:', data)) .catch(err => console.error('错误:', err));Promise链式调用
主要优势是能够构建顺序操作而无需嵌套:
readFilePromise('user.json') .then(userData => { const user = JSON.parse(userData); return dbQueryPromise(`SELECT * FROM posts WHERE user_id=${user.id}`); }) .then(posts => { return writeFilePromise('user_posts.json', JSON.stringify(posts)); }) .then(() => console.log('完成!')) .catch(err => console.error('链式调用中的错误:', err));并行执行:Promise.all
当需要同时执行多个独立操作时:
const promise1 = readFilePromise('file1.txt');const promise2 = readFilePromise('file2.txt');const promise3 = readFilePromise('file3.txt');
Promise.all([promise1, promise2, promise3]) .then(results => { console.log('所有文件已读取:', results); }) .catch(err => console.error('其中一个Promise失败:', err));Promise.all只有在所有Promise都成功完成时才会成功。如果至少有一个失败,则会触发