Promise 面试题集锦
以下是常见的 Promise 面试题及其解析,帮助你深入理解 Promise 的工作原理和使用场景。
基础概念题
1. Promise 的基本状态有哪些?
答案:
- pending(进行中)
- fulfilled(已成功)
- rejected(已失败)
2. Promise 的状态变化是怎样的?
答案:
- 只能从 pending 变为 fulfilled 或从 pending 变为 rejected
- 状态一旦改变就不能再变
代码输出题
3. 下面代码的输出顺序是什么?
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
答案: 1 → 4 → 3 → 2
解析:
- 同步代码优先执行(1,4)
- Promise 的 then 属于微任务(microtask),在当前事件循环末尾执行
- setTimeout 属于宏任务(macrotask),在下一次事件循环执行
4. 下面代码的输出是什么?
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
答案: 1 → 2 → 4 → 3
解析:
- Promise 构造函数是同步执行的
- then 回调是异步的微任务
链式调用题
5. 下面代码的输出顺序是什么?
Promise.resolve()
.then(() => {
console.log('promise1');
return Promise.resolve('promise2');
})
.then((res) => {
console.log(res);
});
Promise.resolve()
.then(() => {
console.log('promise3');
})
.then(() => {
console.log('promise4');
});
答案(可能因浏览器实现略有不同): promise1 → promise3 → promise4 → promise2
解析:
- 第一个 Promise 链的第一个 then 先执行
- 第二个 Promise 链的第一个 then 接着执行
- 由于 return Promise.resolve() 会创建一个新的微任务,所以 promise2 最后输出
错误处理题
6. 下面代码的输出是什么?
Promise.reject('error')
.then(
() => console.log('success'),
(err) => console.log('fail1', err)
)
.catch((err) => console.log('fail2', err));
答案: fail1 error
解析:
- reject 被 then 的第二个参数捕获
- 由于错误已经被处理,catch 不会执行
7. 下面代码的输出是什么?
Promise.resolve()
.then(() => {
throw new Error('error');
})
.then(
() => console.log('success'),
(err) => console.log('fail1', err)
)
.catch((err) => console.log('fail2', err));
答案: fail1 Error: error
解析:
- 第一个 then 抛出错误
- 被第二个 then 的第二个参数捕获
- catch 不会执行因为错误已被处理
综合应用题
8. 实现一个 Promise.retry 方法,当 promise 失败后会重试指定的次数
答案:
Promise.retry = function (promiseFn, times = 3) {
return new Promise(async (resolve, reject) => {
while (times--) {
try {
const result = await promiseFn();
resolve(result);
break;
} catch (err) {
if (!times) reject(err);
}
}
});
};
// 使用示例
function getData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ? resolve('success') : reject('error');
}, 1000);
});
}
Promise.retry(getData, 3)
.then(console.log)
.catch(console.error);
9. 实现一个限制并发数的 Promise 调度器
答案:
class Scheduler {
constructor(max) {
this.max = max;
this.count = 0;
this.queue = [];
}
add(promiseCreator) {
return new Promise((resolve) => {
this.queue.push(() => promiseCreator().then(resolve));
this.run();
});
}
run() {
if (this.count < this.max && this.queue.length) {
this.count++;
const task = this.queue.shift();
task().finally(() => {
this.count--;
this.run();
});
}
}
}
// 使用示例
const scheduler = new Scheduler(2);
const timeout = (time) => new Promise((r) => setTimeout(r, time));
const addTask = (time, order) => {
scheduler.add(() => timeout(time)).then(() => console.log(order));
};
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// 输出:2 3 1 4
高级理解题
10. Promise 和 setTimeout 的执行顺序是怎样的?为什么?
答案:
- Promise 的回调属于微任务(microtask)
- setTimeout 的回调属于宏任务(macrotask)
- 事件循环中,微任务会在当前宏任务执行完后立即执行,而宏任务要等到下一次事件循环
- 因此 Promise.then 总是比 setTimeout 先执行
11. 如何取消一个 Promise?
答案: 原生 Promise 无法直接取消,但可以通过以下方式实现类似效果:
- 使用 AbortController (较新浏览器支持)
const controller = new AbortController();
const signal = controller.signal;
const promise = new Promise((resolve, reject) => {
signal.addEventListener('abort', () => {
reject(new DOMException('Aborted', 'AbortError'));
});
// 正常异步操作
});
// 取消
controller.abort();
- 使用标志变量
let isCancelled = false;
const cancellablePromise = new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
if (isCancelled) {
reject(new Error('Cancelled'));
} else {
resolve('Success');
}
}, 1000);
});
// 取消
isCancelled = true;
这些题目涵盖了 Promise 的核心知识点,理解它们可以帮助你在面试中更好地应对相关问题。