JavaScript异步编程是指在单线程环境下,通过非阻塞方式执行耗时操作(如I/O、定时器等)的技术。由于JS是单线程语言,异步编程可以避免阻塞主线程,提高程序响应能力。
最基础的异步模式,将函数作为参数传递给异步操作。
function fetchData(callback) {
setTimeout(() => {
callback('Data received');
}, 1000);
}
fetchData((data) => {
console.log(data); // 1秒后输出"Data received"
});
ES6引入的异步解决方案,解决了回调地狱问题。
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Data received');
// 或 reject(new Error('Failed'));
}, 1000);
});
}
fetchData()
.then(data => console.log(data))
.catch(error => console.error(error));
ES2017引入的语法糖,使异步代码看起来像同步代码。
async function getData() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error(error);
}
}
getData();
常用于处理多个异步事件。
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('data', (data) => {
console.log('Received:', data);
});
setTimeout(() => {
emitter.emit('data', 'Some data');
}, 1000);
ES6引入,可以暂停和恢复函数执行。
function* asyncGenerator() {
const result = yield new Promise(resolve =>
setTimeout(() => resolve('Done'), 1000)
);
console.log(result);
}
const gen = asyncGenerator();
gen.next().value.then(val => gen.next(val));
setTimeout
, setInterval
fs
模块问题:多层嵌套回调导致代码难以维护。
解决方案:
问题:异步错误难以捕获,特别是多层嵌套时。
解决方案:
.catch()
方法process.on('unhandledRejection')
)问题:多个异步操作同时执行可能导致资源竞争或过载。
解决方案:
Promise.all
(并行执行)Promise.allSettled
(不中断执行)async
的控制流函数问题:未清理的定时器、事件监听器等导致内存无法释放。
解决方案:
clearTimeout
, clearInterval
)在模块顶层直接使用await(ES2022)
// 模块中
const data = await fetchData();
console.log(data);
// 等待所有完成(可能有的失败)
Promise.allSettled([promise1, promise2])
.then(results => {
// 处理所有结果
});
// 竞速模式
Promise.race([promise1, promise2])
.then(firstResult => {
// 处理第一个完成的结果
});
取消异步操作
const controller = new AbortController();
const signal = controller.signal;
fetch(url, { signal })
.then(response => response.json())
.catch(err => {
if (err.name === 'AbortError') {
console.log('Fetch aborted');
}
});
// 取消请求
controller.abort();
JavaScript的异步编程是处理非阻塞操作的核心机制,理解各种异步模式及其适用场景对于开发高效、可维护的应用程序至关重要。