13. 说说你对Promise的理解?Promise.all和Promise.race的区别?
问题解析
Promise是JavaScript异步编程的重要解决方案,理解Promise的原理、状态变化以及静态方法的使用是前端面试中的高频考点。本题主要考察对Promise核心概念的理解以及Promise.all和Promise.race的区别和应用场景。
核心概念
什么是Promise
Promise是ES6引入的异步编程解决方案,它是一个对象,代表了一个异步操作的最终完成(或失败)及其结果值。Promise有三种状态:
- pending(进行中):初始状态,既未完成也未拒绝
- fulfilled(已成功):操作成功完成
- rejected(已失败):操作失败
Promise的特点
- 状态一旦改变,就不会再变:Promise对象的状态改变只有两种可能:从pending变为fulfilled,或者从pending变为rejected
- 链式调用:通过then方法可以进行链式调用,解决回调地狱问题
- 错误捕获:通过catch方法可以统一捕获错误
详细解答
Promise基本用法
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
if (/* 异步操作成功 */) {
resolve(value); // 将Promise状态变为fulfilled
} else {
reject(error); // 将Promise状态变为rejected
}
}, 1000);
});
promise
.then(value => {
// 成功时的回调
console.log(value);
})
.catch(error => {
// 失败时的回调
console.error(error);
})
.finally(() => {
// 无论成功失败都会执行
console.log('完成');
});
Promise.all
Promise.all用于将多个Promise实例包装成一个新的Promise实例。
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);
Promise.all([p1, p2, p3])
.then(values => {
console.log(values); // [1, 2, 3]
})
.catch(error => {
console.error(error);
});
特点:
- 所有Promise都成功时,返回的Promise才会成功,结果是一个数组,按传入顺序排列
- 只要有一个Promise失败,整个Promise.all就失败,返回第一个失败的Promise的原因
Promise.race
Promise.race同样是将多个Promise实例包装成一个新的Promise实例,但只要有一个实例率先改变状态,整个Promise的状态就跟着改变。
const p1 = new Promise((resolve) => {
setTimeout(resolve, 500, 'one');
});
const p2 = new Promise((resolve) => {
setTimeout(resolve, 1000, 'two');
});
Promise.race([p1, p2])
.then(value => {
console.log(value); // 'one',因为p1先完成
});
特点:
- 哪个Promise先完成(无论成功或失败),就采用哪个Promise的结果
- 常用于设置超时处理
Promise.all与Promise.race的区别
| 特性 | Promise.all | Promise.race |
|---|---|---|
| 触发条件 | 所有Promise完成 | 第一个Promise完成 |
| 成功结果 | 所有Promise结果的数组 | 第一个完成的Promise结果 |
| 失败条件 | 任意一个Promise失败 | 第一个完成的Promise失败 |
| 应用场景 | 并行执行多个异步操作 | 竞速、超时控制 |
深入理解
Promise的链式调用
fetch('/api/user')
.then(response => response.json())
.then(user => fetch(`/api/posts/${user.id}`))
.then(response => response.json())
.then(posts => console.log(posts))
.catch(error => console.error(error));
Promise.all的实际应用
// 并行加载多个资源
async function loadResources() {
try {
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/comments').then(r => r.json())
]);
return { users, posts, comments };
} catch (error) {
console.error('加载资源失败:', error);
}
}
Promise.race的超时控制
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), timeout)
)
]);
}
最佳实践
- 始终处理错误:使用catch捕获Promise链中的错误
- 避免Promise嵌套:使用链式调用代替嵌套
- 合理使用Promise.all:并行执行独立的异步操作,提高效率
- 注意错误处理:Promise.all中一个失败会导致全部失败,如需单独处理可使用Promise.allSettled
// 使用Promise.allSettled获取所有结果,无论成功失败
Promise.allSettled([promise1, promise2, promise3])
.then(results => {
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('成功:', result.value);
} else {
console.log('失败:', result.reason);
}
});
});
面试要点
- 能够清晰解释Promise的三种状态及其转换
- 理解Promise如何解决回调地狱问题
- 掌握Promise.all和Promise.race的区别和使用场景
- 了解async/await与Promise的关系(async函数返回Promise)
- 能够手写简单的Promise实现