async/await异步编程
ECMAScript 8 async/await异步编程
ECMAScript 8(ES2017)引入了async/await语法,让基于Promise的异步代码看起来更像同步代码。这种语法糖彻底改变了JavaScript异步编程的方式,使代码更易读、更易维护。
async函数基础
async函数是使用async
关键字声明的函数,它总是返回一个Promise。如果在async函数中返回一个非Promise值,JavaScript会自动将其包装为已解决的Promise。
async function basicAsync() {
return 'Hello Async';
}
basicAsync().then(console.log); // 输出: "Hello Async"
async函数内部可以使用await
关键字暂停执行,直到等待的Promise解决或拒绝。await只能在async函数内部使用。
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
await表达式详解
await表达式会暂停async函数的执行,等待Promise解决,然后恢复async函数的执行并返回解决的值。如果等待的值不是Promise,它会被转换为一个已解决的Promise。
async function awaitDemo() {
const result = await new Promise(resolve => {
setTimeout(() => resolve('Done!'), 1000);
});
console.log(result); // 1秒后输出: "Done!"
}
当Promise被拒绝时,await会抛出拒绝的原因,可以使用try/catch捕获:
async function handleError() {
try {
await Promise.reject('Something went wrong');
} catch (error) {
console.error('Caught error:', error);
}
}
并行执行多个异步操作
虽然await会顺序执行,但我们可以通过一些技巧实现并行:
async function parallelTasks() {
const [user, posts] = await Promise.all([
fetch('/user'),
fetch('/posts')
]);
// 两个请求同时进行,都完成后才继续执行
}
另一种方式是先启动所有Promise,然后再await:
async function parallelAwait() {
const userPromise = fetch('/user');
const postsPromise = fetch('/posts');
const user = await userPromise;
const posts = await postsPromise;
}
错误处理模式
async/await提供了多种错误处理方式:
- 传统的try/catch:
async function tryCatchExample() {
try {
const data = await fetchData();
} catch (error) {
console.error('Fetch failed:', error);
}
}
- 在Promise链末尾添加catch:
async function promiseCatchExample() {
const data = await fetchData().catch(error => {
console.error('Fetch failed:', error);
});
}
- 使用辅助函数包装:
function to(promise) {
return promise.then(data => [null, data]).catch(err => [err]);
}
async function usingHelper() {
const [err, data] = await to(fetchData());
if (err) console.error('Error:', err);
}
async函数与Promise的互操作性
async函数返回Promise,因此可以无缝与现有Promise代码配合使用:
async function getUser(id) {
return { id, name: 'John Doe' };
}
// 在Promise链中使用async函数
getUser(1)
.then(user => console.log(user))
.catch(console.error);
// 在async函数中使用Promise
async function processUser() {
const user = await getUser(1);
console.log(user);
}
常见陷阱与最佳实践
- 避免不必要的await:
// 不好 - 不必要的await
async function slowFunc() {
const result = await doSomething();
return await processResult(result);
}
// 更好
async function fastFunc() {
const result = await doSomething();
return processResult(result); // processResult返回Promise
}
- 循环中的await:
// 顺序执行 - 每个await都会等待前一个完成
async function processArray(array) {
for (const item of array) {
await processItem(item);
}
}
// 并行执行
async function processArrayParallel(array) {
await Promise.all(array.map(item => processItem(item)));
}
- 顶层await:在ES模块中可以直接使用await
// module.js
const data = await fetchData();
export default data;
高级模式
- 重试逻辑:
async function retry(fn, retries = 3, delay = 1000) {
try {
return await fn();
} catch (error) {
if (retries <= 0) throw error;
await new Promise(resolve => setTimeout(resolve, delay));
return retry(fn, retries - 1, delay * 2);
}
}
- 超时控制:
async function withTimeout(promise, timeout) {
let timer;
const timeoutPromise = new Promise((_, reject) => {
timer = setTimeout(() => reject(new Error('Timeout')), timeout);
});
try {
return await Promise.race([promise, timeoutPromise]);
} finally {
clearTimeout(timer);
}
}
- 顺序处理动态生成的Promise:
async function processInOrder(generator) {
for await (const promise of generator) {
try {
const result = await promise;
console.log(result);
} catch (error) {
console.error(error);
}
}
}
async函数与类方法
async函数也可以作为类的方法:
class ApiClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async get(endpoint) {
const response = await fetch(`${this.baseUrl}${endpoint}`);
return response.json();
}
}
// 使用
const client = new ApiClient('https://api.example.com');
client.get('/users').then(console.log);
async生成器函数
ES2018引入了异步生成器函数,可以yield Promise:
async function* asyncGenerator() {
let i = 0;
while (i < 3) {
await new Promise(resolve => setTimeout(resolve, 1000));
yield i++;
}
}
(async () => {
for await (const num of asyncGenerator()) {
console.log(num); // 每秒输出0, 1, 2
}
})();
性能考量
虽然async/await使代码更清晰,但需要注意:
- 每个await都会导致函数暂停,创建额外的Promise对象
- 不必要的顺序await会降低性能
- 错误堆栈可能不如同步代码清晰
// 性能较差的例子
async function poorPerformance() {
const a = await taskA(); // 等待A完成
const b = await taskB(); // 然后等待B完成
return a + b;
}
// 改进版本
async function betterPerformance() {
const [a, b] = await Promise.all([taskA(), taskB()]);
return a + b;
}
浏览器兼容性与转译
虽然现代浏览器都支持async/await,但在旧环境中需要使用Babel等工具转译:
// 转译前的async/await
async function fetchUser() {
const response = await fetch('/user');
return response.json();
}
// 转译后的代码大致如下
function fetchUser() {
return Promise.resolve().then(function() {
return fetch('/user');
}).then(function(response) {
return response.json();
});
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn