JavaScript 异步编程简介
JavaScript 是一种单线程语言,具有非阻塞事件循环(Event Loop)。这意味着在某一时刻只能执行一个操作,但得益于异步机制,我们无需等待慢速任务(如文件读取、API 请求、定时器)完成,即可继续执行其他代码。本文将深入讲解 Node.js 环境中异步编程的所有关键概念,从回调函数到现代 async/await 语法。
异步编程使我们能够构建高性能的服务器端应用程序,在不阻塞主线程的情况下处理数千个请求。理解这一范式是每位 JavaScript 开发者的必备技能。
1. 回调函数与回调地狱问题
历史上,JavaScript 中最早的异步机制是回调函数(callback)——即在异步操作完成后被调用的函数。
简单回调示例
const fs = require('fs');
// 异步读取文件fs.readFile('example.txt', 'utf8', (err, data) => { if (err) { console.error('读取错误:', err); return; } console.log('文件内容:', data);});
console.log('这条消息会比文件内容先出现');当需要执行多个连续的异步操作时,问题就出现了。回调的嵌套层级不断增加,代码变得难以阅读——这种现象被称为“回调地狱”(Callback Hell)。
回调地狱示例
fs.readFile('file1.txt', (err, data1) => { if (err) throw err; fs.readFile('file2.txt', (err, data2) => { if (err) throw err; fs.readFile('file3.txt', (err, data3) => { if (err) throw err; // 处理所有三个文件 console.log(data1 + data2 + data3); }); });});这种代码难以维护、测试和调试。Promise 的出现解决了这一问题。
2. Promise:摆脱回调地狱
Promise(承诺)是一个代表异步操作结果的对象:成功执行(resolve)或失败(reject)。Promise 允许我们构建调用链,使代码更加扁平化和可读。
创建和使用 Promise
// 返回 Promise 的函数const readFilePromise = (filename) => { return new Promise((resolve, reject) => { fs.readFile(filename, 'utf8', (err, data) => { if (err) reject(err); else resolve(data); }); });};
// 使用readFilePromise('file1.txt') .then(data => { console.log('文件已读取:', data); return readFilePromise('file2.txt'); }) .then(data => { console.log('第二个文件:', data); }) .catch(err => { console.error('错误:', err); });Promise.all 和 Promise.race 方法
对于并行执行多个异步操作,有一些实用的静态方法:
Promise.all()— 等待所有 Promise 完成。如果至少有一个失败,整个 Promise.all 将进入 rejected 状态。Promise.race()— 返回第一个完成的 Promise 的结果(成功或失败)。Promise.allSettled()— 等待所有 Promise 完成,返回每个 Promise 的结果(成功或失败)。
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
```