理解 Promise 的关键在于把握其状态和常用方法。
- 三种状态:Promise 有三种状态,分别是 pending(进行中)、fulfilled(已成功) 和 rejected(已失败)。状态一旦改变,就不可逆(从 pending 变为 fulfilled 或 rejected)。
-
链式调用与错误捕获:Promise 可以链式调用,通过
.then()方法处理成功情况,通过.catch()方法捕获链中发生的任何错误,这使得异步代码更易读,避免了"回调地狱"。 -
常用静态方法:
-
Promise.all([...]):接收一个 Promise 实例的数组。只有当所有 Promise 都成功完成时,它才成功,返回值是所有 Promise 结果组成的数组;如果其中有一个被拒绝,Promise.all会立即拒绝,并返回第一个失败的原因。 -
Promise.race([...]):同样是接收一个 Promise 数组,但其中任何一个 Promise 完成(无论成功或失败)或拒绝时,它就会随之完成或拒绝,并返回那个最快改变的 Promise 的结果。
-
💡 RN 中 Promise 的应用场景
在 React Native 中,Promise 常用于以下场景:
- RN 与原生模块交互:RN 调用原生模块(Native Modules)时,原生端可以通过 Promise 将结果返回给 RN 的 JavaScript 端。这种方式比传统的 Callback 更清晰,也便于处理异步成功或失败。
-
网络请求:RN 中的
fetch函数或第三方库(如axios)广泛使用 Promise 来处理 HTTP 请求。
🧠 Promise 面试题选析
面试中常会考察对 Promise 概念和运行机制的理解,以下是一些典型题目:
1. 基础输出与状态变化
这类问题主要考察对 Promise 状态、链式调用以及事件循环中微任务执行顺序的理解。
-
题目示例:
console.log(1); new Promise((resolve, reject) => { console.log(2); resolve(); }).then(() => { console.log(3); }); console.log(4);输出顺序是 1, 2, 4, 3。原因是:
new Promise中的执行器函数是同步执行的,所以先输出 1 和 2。resolve()后将.then()中的回调加入微任务队列,等待同步任务(输出 4)执行完毕后再执行,因此最后输出 3。 -
错误处理与"穿透":
Promise.resolve('Success!') .then(data => { data.toUpperCase() // 注意:这里没有 return }) .then(data => { console.log(data) // 输出:undefined })如果
.then()中的函数没有显式地return一个值,后续.then()接收到的就是undefined。
2. 手写实现类
这类问题考察对 Promise 原理和用法的深入理解。
-
实现
Promise.first():
Promise.first()并不是 ES 标准方法,其目标是:给定一组 Promise,只要其中第一个成功完成(fulfilled) 的 Promise 出现,就忽略后续的任何拒绝和完成。这与Promise.race不同,race是第一个" settled "(无论成功或失败)的。一个简单的实现思路是:用一个数组记录失败的原因,当成功完成的 Promise 数量达到要求或者所有 Promise 都已结束时,决定外层 Promise 是完成还是拒绝。
Promise.first = function (promises) { let rejections = []; return new Promise((resolve, reject) => { promises.forEach(promise => { promise.then(resolve).catch(error => { rejections.push(error); if (rejections.length === promises.length) { reject(new AggregateError(rejections, "All promises were rejected")); } }); }); }); }; -
实现顺序执行:
给你一个包含多个异步操作(返回 Promise)的数组,如何让它们顺序执行,即一个完成后才开始下一个?function order(promises) { let result = []; let sequence = Promise.resolve(); for (let promise of promises) { sequence = sequence.then(() => promise).then(data => result.push(data)); } return sequence.then(() => result); }核心思路是:构建一个 Promise 链,每个环节都在上一个完成后执行当前 Promise。需要注意的是,这种方式虽然保证了执行顺序,但数组中的 Promise 在传入时可能已经开始,若要真正"串行"发起,需在链中动态创建 Promise。
// 推荐写法(函数数组 + for/await) async function runInOrder(tasks: (() => Promise<any>)[]) { const results = []; for (const task of tasks) { results.push(await task()); } return results; } const task1 = () => new Promise(resolve => setTimeout(() => resolve('任务1完成'), 1000)); const task2 = () => new Promise(resolve => setTimeout(() => resolve('任务2完成'), 500)); const task3 = () => new Promise(resolve => setTimeout(() => resolve('任务3完成'), 200)); runInOrder([task1, task2, task3]).then(results => { console.log(results); // ['任务1完成', '任务2完成', '任务3完成'] });传入的是返回 Promise 的函数数组,每个异步操作只有在前一个完成后才开始,真正实现串行。
3. 并发控制
有时需要控制并发请求的数量,例如有 8 个图片 URL 要请求,但要求同时最多 3 个请求。
一种思路是:初始化一个"执行池",当池中有空位且还有剩余任务时,就取出一个新任务执行。任务完成后再从池中移除,并尝试添加新任务。
// 限制并发数的异步加载
function limitLoad(urls, limit) {
let executing = 0;
let index = 0;
const results = [];
function run(url) {
if (index >= urls.length) return Promise.resolve();
executing++;
return loadImg(url) // 你的异步函数,返回 Promise
.then(result => {
results.push(result);
executing--;
if (index < urls.length) {
return run(urls[index++]);
}
});
}
const initialPromises = [];
for (let i = 0; i < limit && i < urls.length; i++, index++) {
initialPromises.push(run(urls[i]));
}
return Promise.allSettled(initialPromises).then(() => results);
}
最佳实践建议使用 async/await + Promise.all + 控制池的方式,代码更易维护和理解。如下:
const results: any[] = [];
let i = 0;
// 工作池
const pool: Promise<void>[] = [];
// 启动 limit 个并发任务
const enqueue = async () => {
while (i < urls.length) {
const currentIndex = i++; // 为了避免异步任务里用到的索引被后续循环修改,确保每个任务的索引唯一且正确。
const p = loadImg(urls[currentIndex])
.then(res => {
results[currentIndex] = res; // 保证结果顺序
});
pool.push(p);
// 控制并发数
if (pool.length >= limit) {
await Promise.race(pool);
// 移除已完成的任务
pool.splice(pool.findIndex(promise => promise === p), 1);
}
}
};
await enqueue(); // 等待所有任务都被加入 pool,开始执行
await Promise.all(pool); // 等待剩余任务完成
return results;
}
✅ 面试准备建议
-
理解概念:务必理解 Promise 的状态、链式调用、错误冒泡/捕获(
.catch)以及静态方法all、race、allSettled、any的区别。 -
熟悉事件循环:明白 Promise 的回调属于微任务,以及它们与宏任务(如
setTimeout)的执行优先级。 -
动手练习:多写代码,尝试手写 Promise 的简单实现、
all或race等方法,以及处理各种异步流程控制。 -
结合 RN 场景思考:思考在 RN 开发中,哪些地方用到了 Promise,比如网络请求
fetch、与原生模块的通信、异步存储等,理解其在实际项目中的应用。












网友评论