JavaScript异步编程
介绍
JavaScript是单线程语言,但为了处理耗时操作(如网络请求、文件读写等),需要使用异步编程模式。异步编程使JavaScript能够在执行耗时操作时不阻塞主线程,提高应用的响应性和性能。
原理
JavaScript异步编程的工作原理:
- 单线程执行模型:JavaScript引擎一次只能执行一个任务
- 调用栈(call stack):存储正在执行的函数调用
- 事件队列(event queue):存储等待执行的异步任务回调
- 事件循环(event loop):不断检查调用栈是否为空,如果为空则从事件队列中取出任务执行
- 异步任务分为宏任务(macro task)和微任务(micro task),微任务优先级高于宏任务
图示
// 回调函数
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: '数据' };
callback(null, data);
}, 1000);
}
// Promise
function fetchDataWithPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { id: 1, name: '数据' };
resolve(data);
// reject(new Error('获取数据失败'));
}, 1000);
});
}
// async/await
async function fetchDataWithAsync() {
try {
const data = await fetchDataWithPromise();
return data;
} catch (error) {
console.error('获取数据失败:', error);
}
}
实例
异步编程示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript异步编程示例</title>
<style>
.result {
margin: 10px 0;
padding: 10px;
border: 1px solid #ccc;
}
.success {
background-color: #d4edda;
color: #155724;
}
.error {
background-color: #f8d7da;
color: #721c24;
}
</style>
</head>
<body>
<h1>JavaScript异步编程示例</h1>
<button id="btn-callback">回调函数</button>
<button id="btn-promise">Promise</button>
<button id="btn-async">async/await</button>
<button id="btn-parallel">并行执行</button>
<div id="results"></div>
<script>
const btnCallback = document.getElementById('btn-callback');
const btnPromise = document.getElementById('btn-promise');
const btnAsync = document.getElementById('btn-async');
const btnParallel = document.getElementById('btn-parallel');
const results = document.getElementById('results');
// 模拟异步API调用
function mockApiCall(delay, value, shouldFail = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail) {
reject(new Error(`API调用失败: ${value}`));
} else {
resolve(`API结果: ${value}`);
}
}, delay);
});
}
// 显示结果
function showResult(message, isSuccess = true) {
const resultElement = document.createElement('div');
resultElement.className = `result ${isSuccess ? 'success' : 'error'}`;
resultElement.textContent = message;
results.appendChild(resultElement);
// 滚动到最新结果
resultElement.scrollIntoView({ behavior: 'smooth' });
}
// 清空结果
function clearResults() {
results.innerHTML = '';
}
// 回调函数示例
btnCallback.addEventListener('click', function() {
clearResults();
showResult('开始执行回调函数...');
mockApiCall(1000, '回调数据')
.then(data => {
showResult(data);
return mockApiCall(1000, '回调数据2');
})
.then(data2 => {
showResult(data2);
})
.catch(error => {
showResult(error.message, false);
});
});
// Promise示例
btnPromise.addEventListener('click', function() {
clearResults();
showResult('开始执行Promise...');
mockApiCall(1000, 'Promise数据')
.then(data => {
showResult(data);
return mockApiCall(1000, 'Promise数据2');
})
.then(data2 => {
showResult(data2);
})
.catch(error => {
showResult(error.message, false);
});
});
// async/await示例
btnAsync.addEventListener('click', async function() {
clearResults();
showResult('开始执行async/await...');
try {
const data = await mockApiCall(1000, 'async/await数据');
showResult(data);
const data2 = await mockApiCall(1000, 'async/await数据2');
showResult(data2);
} catch (error) {
showResult(error.message, false);
}
});
// 并行执行示例
btnParallel.addEventListener('click', async function() {
clearResults();
showResult('开始并行执行...');
try {
const promise1 = mockApiCall(1500, '并行数据1');
const promise2 = mockApiCall(1000, '并行数据2');
const promise3 = mockApiCall(2000, '并行数据3');
const [result1, result2, result3] = await Promise.all([promise1, promise2, promise3]);
showResult(result1);
showResult(result2);
showResult(result3);
showResult('所有并行任务完成');
} catch (error) {
showResult(error.message, false);
}
});
</script>
</body>
</html>
专业解决方案
异步编程模式
- 回调函数:最早的异步编程模式,容易导致回调地狱
- Promise:ES6引入,解决回调地狱问题,提供链式调用
- async/await:ES2017引入,基于Promise的语法糖,使异步代码更接近同步代码风格
- 生成器(Generator):通过yield关键字控制函数执行流程
- 可观察对象(Observable):适合处理连续的异步数据流
Promise API
- Promise.resolve():创建一个已解决的Promise
- Promise.reject():创建一个已拒绝的Promise
- Promise.all():等待所有Promise完成,返回结果数组
- Promise.race():等待第一个完成的Promise,返回其结果
- Promise.allSettled():等待所有Promise完成,返回包含所有Promise结果的数组
- Promise.any():等待第一个成功的Promise,返回其结果
错误处理
- 使用try/catch捕获同步错误
- 使用Promise的catch()方法捕获异步错误
- 在async/await中使用try/catch捕获错误
- 为全局未捕获的Promise拒绝添加处理程序
- 避免在异步代码中使用throw,除非有明确的错误处理机制
性能优化
- 并行执行独立的异步任务(Promise.all)
- 避免过多的Promise链式调用
- 使用Promise缓存重复的异步操作结果
- 合理设置超时处理(Promise.race + setTimeout)
- 考虑使用防抖(debounce)和节流(throttle)优化频繁触发的异步操作
工具推荐
- Axios:基于Promise的HTTP客户端
- Bluebird:功能丰富的Promise库
- Async.js:提供多种异步流程控制函数
- RxJS:响应式编程库,处理异步数据流
- Lodash:提供debounce和throttle等实用函数