异步编程
JavaScript是单线程语言,异步编程是处理耗时操作(如网络请求、文件I/O等)的关键技术。
回调函数
回调函数是异步编程的传统方式,它是一个作为参数传递给另一个函数的函数:
javascript
// 示例:读取文件
const fs = require('fs');
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) {
console.error('读取文件失败:', err);
return;
}
console.log('文件内容:', data);
});
console.log('读取文件操作已发起');
回调地狱
当有多个嵌套的异步操作时,会导致代码难以阅读和维护,这被称为回调地狱:
javascript
fs.readFile('file1.txt', 'utf8', (err, data1) => {
if (err) throw err;
fs.readFile('file2.txt', 'utf8', (err, data2) => {
if (err) throw err;
fs.writeFile('output.txt', data1 + data2, (err) => {
if (err) throw err;
console.log('文件已合并');
});
});
});
Promise
Promise是ES6引入的解决异步编程的方案,它代表一个异步操作的最终完成(或失败)及其结果值:
Promise的创建
javascript
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve("数据获取成功");
} else {
reject("数据获取失败");
}
}, 1000);
});
};
Promise的使用
javascript
fetchData()
.then(data => {
console.log(data);
return "处理后的数据";
})
.then(processedData => {
console.log(processedData);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log("操作完成");
});
Promise的静态方法
Promise.all
等待所有Promise完成:
javascript
const promises = [
fetch('https://api.example.com/data1'),
fetch('https://api.example.com/data2'),
fetch('https://api.example.com/data3')
];
Promise.all(promises)
.then(responses => {
return Promise.all(responses.map(res => res.json()));
})
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
Promise.race
等待第一个完成的Promise:
javascript
const promise1 = new Promise(resolve => setTimeout(resolve, 1000, 'one'));
const promise2 = new Promise(resolve => setTimeout(resolve, 500, 'two'));
Promise.race([promise1, promise2])
.then(result => {
console.log(result); // 'two'
});
Promise.allSettled
等待所有Promise完成,无论成功或失败:
javascript
const promises = [
Promise.resolve(1),
Promise.reject('错误'),
Promise.resolve(3)
];
Promise.allSettled(promises)
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.log('失败:', result.reason);
}
});
});
Promise.any
等待第一个成功的Promise:
javascript
const promises = [
Promise.reject('错误1'),
Promise.resolve(2),
Promise.reject('错误3')
];
Promise.any(promises)
.then(result => {
console.log('成功:', result); // 2
})
.catch(error => {
console.error('所有Promise都失败了:', error);
});
async/await
async/await是ES2017引入的语法糖,基于Promise,使异步代码看起来像同步代码:
async函数
async关键字用于声明异步函数,异步函数总是返回一个Promise:
javascript
async function fetchData() {
return "数据";
// 相当于 return Promise.resolve("数据");
}
fetchData().then(data => console.log(data)); // "数据"
await关键字
await关键字用于等待一个Promise完成,只能在async函数内部使用:
javascript
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error('数据获取失败:', error);
}
}
fetchData();
并行执行
javascript
async function fetchMultipleData() {
const [data1, data2] = await Promise.all([
fetch('https://api.example.com/data1').then(res => res.json()),
fetch('https://api.example.com/data2').then(res => res.json())
]);
console.log('数据1:', data1);
console.log('数据2:', data2);
}
fetchMultipleData();
事件循环
理解事件循环对于掌握异步编程至关重要:
执行栈
JavaScript引擎使用执行栈来管理函数调用。
任务队列
异步操作完成后,其回调函数被放入任务队列:
- 宏任务(Macro Task):setTimeout, setInterval, setImmediate, I/O, UI渲染
- 微任务(Micro Task):Promise, process.nextTick, MutationObserver
事件循环流程
- 执行执行栈中的同步代码
- 执行所有微任务队列中的任务
- 渲染UI
- 从宏任务队列中取出一个任务执行
- 重复步骤2-4
javascript
console.log('1');
setTimeout(() => {
console.log('2');
}, 0);
Promise.resolve().then(() => {
console.log('3');
});
console.log('4');
// 输出顺序:1, 4, 3, 2
异步编程最佳实践
- 使用async/await:使代码更易读和维护
- 处理错误:使用try/catch或.catch()处理异步操作的错误
- 避免阻塞主线程:将耗时操作放在Web Worker或Node.js的子进程中
- 限制并发请求:避免同时发起过多网络请求
- 使用Promise.all处理并行操作:提高性能
学习资源
继续学习:DOM操作
最后更新:2026-02-08