JavaScript
Promise的基本概念
Promise 是一种代表异步操作最终完成(或失败)及其结果值的机制。它可以用来替代传统的回调函数,提供更加直观和易于管理的异步代码结构。
Promise 有三种状态:
- Pending(待定):表示异步操作还未完成,结果尚未可得。
- Fulfilled(已兑现):表示异步操作已成功完成,且返回结果。
- Rejected(已拒绝):表示异步操作失败,且返回错误信息。
Promise的核心方法
-
then():用于处理 Promise 成功(fulfilled)后的回调。当 Promise 被解决(resolved)时,它会执行传入的回调函数,并返回一个新的 Promise。promise.then(result => { console.log('成功:', result); }).catch(error => { console.log('失败:', error); }); -
catch():用于处理 Promise 失败(rejected)后的回调,返回一个新的 Promise。 -
finally():不论 Promise 最终是 resolved 还是 rejected,finally()总会执行,通常用来执行清理操作。
Promise 链式调用
Promise 提供了链式调用的能力。因为每个 then() 和 catch() 都返回一个新的 Promise,所以你可以把多个操作串联在一起。每次 then() 的返回值都会作为下一个 then() 的输入。
fetchData()
.then(data => {
console.log('数据获取成功', data);
return processData(data); // 返回一个新的 Promise
})
.then(processedData => {
console.log('数据处理成功', processedData);
})
.catch(error => {
console.error('操作失败', error);
})
.finally(() => {
console.log('操作结束');
});
Promise.all 和 Promise.race
-
Promise.all():接受一个包含多个 Promise 的数组,只有当所有 Promise 都成功时,Promise.all()才会返回成功。如果其中有一个失败,整个Promise.all()会失败。Promise.all([promise1, promise2, promise3]) .then(results => { console.log('所有操作都成功:', results); }) .catch(error => { console.error('其中一个操作失败:', error); }); -
Promise.race():返回第一个完成(不管是 resolved 还是 rejected)的 Promise 的结果。Promise.race([promise1, promise2, promise3]) .then(result => { console.log('第一个完成:', result); }) .catch(error => { console.error('第一个失败:', error); });
异常处理与链式错误捕获
`
Promise 提供了强大的错误处理能力。当链式调用中某个 Promise 被拒绝,catch() 会捕获到错误,并可以继续传递或处理后续的错误。这避免了传统回调方式中的错误被吞掉的情况。
doSomething()
.then(result => {
return doAnotherThing(result);
})
.catch(error => {
console.error('出错了:', error);
});
Promise的优化与进阶
-
异步操作的串行和并行处理:
- 串行处理:多个异步操作按顺序执行,后一个操作依赖于前一个操作的结果。
- 并行处理:多个异步操作同时执行,只有当所有操作都完成时才继续。
使用
Promise.all()或Promise.allSettled()进行并行操作。 -
async/await与 Promise 的关系:async和await是基于 Promise 实现的语法糖,它使得异步代码更加简洁和同步化。通过await可以暂停函数的执行直到 Promise 完成,从而避免了大量的回调函数或链式then()。async function fetchData() { try { let data = await fetch('https://api.example.com/data'); let result = await data.json(); console.log(result); } catch (error) { console.error(error); } }
Promise 内部实现原理
Promise 本质上是一个状态机,在处理异步操作时,会将状态(pending、fulfilled、rejected)存储在内部。每次调用 then() 或 catch() 时,都会返回一个新的 Promise,且通过 .resolve() 或 .reject() 来改变状态。通过微任务队列来实现异步执行,以确保 then() 和 catch() 的回调总是在当前执行栈清空后执行。
一些常见的 Promise 面试题及其答案如下:
1. Promise 与回调函数的区别是什么?
-
回调函数:通过将函数作为参数传递给另一个函数来处理异步操作的结果。它可能导致“回调地狱”(callback hell),使得代码难以维护和理解。
-
Promise:是一个代表异步操作最终完成或失败的值。它允许通过.then()、.catch()和.finally()链式调用,从而避免了嵌套回调,并使代码更加简洁和可读。
2. Promise 的三种状态是什么?
Promise 具有三种状态:
-
Pending(待定):表示异步操作还未完成,结果尚不可得。
-
Fulfilled(已兑现):表示异步操作完成并成功返回结果。
-
Rejected(已拒绝):表示异步操作失败,返回错误信息。
3. 如何在 Promise 中处理错误?
可以通过 .catch() 或 .then() 的第二个回调函数来处理错误:
promise.then(result => {
console.log(result);
}).catch(error => {
console.error(error); // 处理错误
});
或者:
promise.then(result => {
console.log(result);
}, error => {
console.error(error); // 处理错误
});
4. 什么是 Promise.all() 和 Promise.race()?它们有什么区别?
-
Promise.all():接受一个包含多个 Promise 的数组,只有当所有 Promise 都成功时,Promise.all()才会返回成功。如果其中任何一个 Promise 被拒绝,整个Promise.all()会失败。Promise.all([promise1, promise2, promise3]) .then(results => { console.log('所有 Promise 都成功:', results); }) .catch(error => { console.error('其中一个失败:', error); }); -
Promise.race():返回第一个完成(无论是 fulfilled 还是 rejected)的 Promise 的结果,不管其他 Promise 是否已完成。Promise.race([promise1, promise2, promise3]) .then(result => { console.log('第一个完成的 Promise:', result); }) .catch(error => { console.error('第一个失败的 Promise:', error); });
5. 如何用 Promise 实现串行和并行执行异步任务?
-
串行执行:可以通过在
.then()中返回新的 Promise 来实现串行执行,后续的异步操作依赖于前一个操作的结果。promise1() .then(result => { return promise2(result); // 等待 promise1 执行完毕后再执行 promise2 }) .then(result2 => { console.log(result2); }); -
并行执行:可以通过
Promise.all()来实现并行执行多个异步任务,所有任务同时开始,只有当所有任务都完成时,才会继续执行后续操作。Promise.all([promise1(), promise2()]) .then(results => { console.log('所有任务都完成:', results); });
6. 解释 async/await 和 Promise 的关系。
async/await 是基于 Promise 的语法糖。它使得异步代码看起来像是同步代码,从而提高可读性。
async标记函数为异步,返回一个Promise。await用于等待一个Promise,它会暂停函数的执行,直到Promise完成。
示例:
async function fetchData() {
try {
let result = await fetch('https://api.example.com');
let data = await result.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
7. 解释 Promise 的内部实现原理。
Promise 内部使用了一个状态机,管理 pending、fulfilled 和 rejected 三种状态。每次调用 .then() 或 .catch() 时,都会返回一个新的 Promise,并且根据原 Promise 的状态来决定如何处理结果:
- 如果 Promise 已经解决,
then()或catch()会立即执行回调。 - 如果 Promise 还在
pending状态,回调函数会被添加到微任务队列(microtask queue)中,并在当前任务执行完之后执行。
8. 如何处理 Promise 的并发限制问题?
当你需要限制并发的 Promise 数量时,可以使用 async/await 和 Promise.all(),或者使用第三方库如 p-limit 或自己实现一个并发限制器。例如,控制并发数量:
async function runTasksWithLimit(tasks, limit) {
const result = [];
const executing = [];
for (const task of tasks) {
const p = task().then(res => result.push(res));
executing.push(p);
if (executing.length >= limit) {
await Promise.race(executing); // 等待最快的任务完成
executing.splice(executing.findIndex(p => p === p), 1);
}
}
await Promise.all(executing); // 等待剩余的任务完成
return result;
}
9. Promise 中的 resolve 和 reject 是什么?
-
resolve(value):表示Promise成功解决,并返回一个值(可以是任意类型,包括另一个 Promise)。 -
reject(reason):表示Promise失败,并返回一个拒绝原因(通常是一个错误对象)。
10. Promise 在 setTimeout() 中的应用是什么?
setTimeout() 会在指定的延迟后执行一个回调,而 Promise 可以用于更优雅的延迟操作。例如,将 setTimeout() 封装为一个返回 Promise 的函数:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
delay(1000).then(() => {
console.log('1秒钟后执行');
});