跳到主要内容

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等实用函数