async 和 await
async 和 await 是用于简化处理异步代码的语法,使得异步代码看起来更像同步代码。 它们基于 Promises,可以更直观地管理异步任务,避免复杂的 .then() 和 .catch() 回调地狱。
语法
async 用于声明一个函数,该函数始终返回一个 Promise 对象。
async function foo() {
return 'Hello, World!';
}
console.log(foo()); // Promise {<fulfilled>: 'Hello, World!'}在 async 函数体内允许使用 await 关键字,用于暂停 async 函数的执行,直到 await 后的 Promise 对象的状态变为 fulfilled 或 rejected 后,返回其结果并继续执行。
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
}
fetchData();在上面的例子中,fetchData 函数会等待 fetch 请求返回结果后再继续执行,使函数体内部的代码 看起来是同步执行的,同时避免了回调地狱。
回调地狱
回调地狱是指多个异步操作嵌套在一起,当嵌套层级过深时,使得代码难以阅读和维护的现象。
使用 Promise 链式调用避免回调地狱
通过链式调用 .then() 方法来实现依次执行,但如果链过长,仍然不够直观。
function fetchData1() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Fetched Data 1');
resolve('Data 1');
}, 1000);
});
}
function fetchData2(data1) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Fetched Data 2 with ' + data1);
resolve('Data 2');
}, 1000);
});
}
function fetchData3(data2) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Fetched Data 3 with ' + data2);
resolve('Data 3');
}, 1000);
});
}
fetchData1()
.then(data1 => fetchData2(data1))
.then(data2 => fetchData3(data2))
.then(data3 => console.log('All data fetched:', data3))
.catch(err => console.error(err));使用 async 和 await 避免回调地狱
极大简化了基于 Promise 的代码逻辑,让异步操作看起来更像同步代码,使得代码结构更清晰,易于维护。
function fetchData1() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Fetched Data 1');
resolve('Data 1');
}, 1000);
});
}
function fetchData2(data1) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Fetched Data 2 with ' + data1);
resolve('Data 2');
}, 1000);
});
}
function fetchData3(data2) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Fetched Data 3 with ' + data2);
resolve('Data 3');
}, 1000);
});
}
// 使用 async/await 简化 Promise 链
async function fetchAllData() {
try {
const data1 = await fetchData1();
const data2 = await fetchData2(data1);
const data3 = await fetchData3(data2);
console.log('All data fetched:', data3);
} catch (err) {
console.error(err);
}
}
fetchAllData();执行顺序
async 函数内部代码的执行顺序在不同情况下会有细微的差别。
无 await 关键字时
当 async 函数内部没有 await 关键字时,它的行为基本上和普通函数没有区别,但仍然会返回一个 Promise。 该 Promise 对象的状态为 fulfilled,并且返回值为函数的返回值。
async function example() {
console.log('Inside async function');
}
console.log('Start');
example();
console.log('End');
// 输出:
// Start
// Inside async function
// End有 await 关键字时
当 async 函数内部有 await 关键字时,有两种情况
- 如果后面是一个
Promise对象时,await会暂停等待该Promise解决,为异步执行 - 如果后面是一个非
Promise值(如常量、非 Promise 对象)时,await会立即返回该值,为同步执行
两种情况相同的地方在于,函数的剩余部分会被加入微任务队列,在当前同步代码执行完后执行。
// Promise 对象
async function example() {
console.log('Inside async function');
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('After await');
}
console.log('Start');
example();
console.log('End');
// 输出:
// Start
// Inside async function
// End
// After await
// 常量
async function example() {
console.log('Before await');
await 42; // 非 Promise 值
console.log('After await');
}
console.log('Start');
example();
console.log('End');
// 输出:
// Start
// Before await
// End
// After await补充
从本质上看,async/await 与 generator 有很多相似之处。可以说 async/await 是对 generator 模式的进一步简化和封装,是一种语法糖。
在 generator 中通过 yield 暂停执行,返回一个迭代器对象,通过手动调用 next() 恢复执行。
而 async/await 通过 await 暂停执行,通过 Promise 对象的状态变化恢复执行。
async/await 本质上是封装了这种模式,以简化写法,并且自动执行 next(),使得代码更加简洁易读。
