面试题答案
一键面试Async/Await与Promise的关系
- Async/Await基于Promise:Async函数是一个异步函数,它返回一个Promise对象。在async函数内部,await只能用于Promise对象上,它暂停async函数的执行,等待Promise解决(resolved)或拒绝(rejected),然后恢复async函数的执行并返回已解决的值或抛出被拒绝的原因。
- 语法糖:Async/Await本质上是对Promise的语法糖,使异步代码看起来更像同步代码,极大地提高了代码的可读性。
各自的优势
- Promise优势:
- 链式调用:通过.then()方法实现链式调用,使得多个异步操作可以按顺序执行,每个.then()方法返回一个新的Promise,方便对异步操作进行组合和管理。
- 支持并行执行:可以使用Promise.all()或Promise.race()等方法来处理多个Promise并行执行的情况。
- Async/Await优势:
- 代码更简洁直观:使用类似同步代码的结构,避免了Promise链式调用中可能出现的“回调地狱”问题,使代码逻辑更加清晰。
- 错误处理更简单:在async函数中,可以使用try...catch块来捕获错误,比Promise的.catch()方法在某些情况下更直观和统一。
适用场景
- Promise:适用于需要处理多个异步操作并行执行,或者对异步操作的控制流要求较为灵活的场景,例如同时发起多个API请求并等待所有请求完成。
- Async/Await:适用于需要按顺序依次执行多个异步操作,并且希望代码结构更接近同步代码,提高代码可读性的场景,比如需要依次调用多个依赖的API。
依次调用多个异步API的实现
假设存在三个异步API函数 api1()
、api2()
、api3()
,每个函数返回一个Promise对象。
使用Promise实现
function api1() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('api1执行完毕');
resolve('api1结果');
}, 1000);
});
}
function api2(result1) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('api2执行完毕,依赖api1结果:', result1);
resolve('api2结果');
}, 1000);
});
}
function api3(result2) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('api3执行完毕,依赖api2结果:', result2);
resolve('api3结果');
}, 1000);
});
}
api1()
.then(result1 => api2(result1))
.then(result2 => api3(result2))
.then(finalResult => {
console.log('最终结果:', finalResult);
})
.catch(error => {
console.error('发生错误:', error);
});
使用Async/Await实现
function api1() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('api1执行完毕');
resolve('api1结果');
}, 1000);
});
}
function api2(result1) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('api2执行完毕,依赖api1结果:', result1);
resolve('api2结果');
}, 1000);
});
}
function api3(result2) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('api3执行完毕,依赖api2结果:', result2);
resolve('api3结果');
}, 1000);
});
}
async function main() {
try {
const result1 = await api1();
const result2 = await api2(result1);
const finalResult = await api3(result2);
console.log('最终结果:', finalResult);
} catch (error) {
console.error('发生错误:', error);
}
}
main();
代码可读性和错误处理差异
- 代码可读性:
- Promise:链式调用在处理多个异步操作时,随着操作数量的增加,代码会变得冗长,尤其是嵌套多层时,会出现“回调地狱”问题,使得代码逻辑不清晰。
- Async/Await:使用同步代码的结构,通过
await
暂停异步操作,代码结构更清晰,易于理解和维护,提高了代码的可读性。
- 错误处理:
- Promise:通过
.catch()
方法捕获整个链式调用中的错误,但如果链式调用较长,定位具体出错位置可能相对困难,并且每个.then()
方法内部的同步代码错误无法被.catch()
捕获,需要在每个.then()
内部单独处理。 - Async/Await:可以使用
try...catch
块捕获整个async函数内的所有错误,无论是异步操作还是同步代码的错误,都可以统一处理,使错误处理更直观和简洁。
- Promise:通过