Promise 面试题
一、最难的 Promise 面试题
Promise 相关面试题中,难度较高的通常考察对异步流程、事件循环的深刻理解和灵活的运用能力。以下是一个经典且具有一定难度的面试题:
题目:
请描述以下代码的输出结果及原因:
javascript
console.log("start");
new Promise((resolve, reject) => {
console.log("promise1");
resolve();
})
.then(() => {
console.log("then1");
return Promise.resolve();
})
.then(() => {
console.log("then2");
});
setTimeout(() => {
console.log("timeout");
}, 0);
console.log("end");
正确答案:
输出顺序为:
start
promise1
end
then1
then2
timeout
原因解释:
- JavaScript 中执行顺序为同步代码优先执行,异步代码后执行。
- 首先,
console.log('start')
输出"start"
。 - 执行
new Promise()
中构造函数里的同步代码,输出"promise1"
。 resolve()
被调用,Promise 状态为 fulfilled,此时 then 方法回调被放入微任务队列等待执行。setTimeout()
中的回调被放入宏任务队列。- 执行最后一个同步语句
console.log('end')
,输出"end"
。 - 同步代码执行完成,开始执行微任务,依次输出
"then1"
和"then2"
。 - 微任务执行完成,开始执行宏任务,最后输出
"timeout"
。
面试考察重点:
- Promise 状态及 then 链的执行顺序
- 微任务与宏任务队列执行机制
- JavaScript Event Loop 工作原理
面试官往往会以这种问题检测候选人对异步、事件循环的精确理解。
二、变种
这里给出一个较为复杂的、Promise 面试题的变种版本:
题目:
请描述下面代码的输出结果及原因:
javascript
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0);
Promise.resolve()
.then(function () {
console.log("promise1");
})
.then(function () {
console.log("promise2");
});
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
console.log("script end");
正确答案:
代码输出结果为:
script start
async1 start
async2
script end
promise1
async1 end
promise2
setTimeout
原因分析:
JavaScript 的执行机制(同步代码 → 微任务 → 宏任务):
同步执行
首先执行
console.log('script start')
输出script start
。然后遇到
setTimeout
将其回调放入宏任务队列中。遇到
Promise.resolve().then(...)
,then 回调进入微任务队列。执行函数
async1()
:输出
async1 start
。调用
async2()
:- 输出
async2
。
- 输出
遇到
await async2()
时,await
后面表达式是立即完成(因为 async2 已执行完),但 JS 会将后续的代码放到微任务队列中。
执行
console.log('script end')
输出script end
。
微任务阶段 按照微任务队列先进先出顺序:
- 首先输出
promise1
(Promise.resolve().then
回调)。 - 然后执行之前 await 后面的语句,输出
async1 end
。 - 接着执行链式的第二个
.then
,输出promise2
。
- 首先输出
宏任务阶段
- 最后执行宏任务队列里的
setTimeout
,输出setTimeout
。
- 最后执行宏任务队列里的
面试官考察重点:
- async/await 与 Promise 在事件循环中的关系。
- 微任务与宏任务的执行顺序与差异。
- await 后面的表达式如果立即 resolve,后续代码进入微任务队列。
这类问题体现了候选人是否真正深入理解 JavaScript 异步运行的内在逻辑。