Question 1(基础概念)

中文: 请你解释一下什么是 Promise?它主要解决了什么问题?

English: Can you explain what a Promise is and what problem it is designed to solve?

 

标准答案(中文)

Promise 是一个用于表示异步操作结果的对象,它本质是一个状态机 + 回调容器 它代表一个未来会完成(或失败)的操作结果

Promise 主要解决的是:

  1. 回调地狱(callback hell)问题
  2. 让异步代码更加清晰、可读、可维护
  3. 提供统一的错误处理机制

Promise 有三种状态:

一旦状态从 pending 变为 fulfilled 或 rejected,就不可再改变(状态不可逆)

作用:Promise能够让你不用关心任务什么时候结束(因为用到Promise的地方,可能都会有异步操作),在new的时候告诉它执行什么任务,之后只需要通过 then 告诉它“之前的任务结束之后该干嘛”。

 

面试加分点:Promise 本质上是一个状态机,并且它的回调(then/catch)是放入微任务队列执行的。

 

Standard Answer (English)

A Promise is an object representing the result of an asynchronous operation, essentially a state machine + callback container. It represents the eventual completion or failure of an operation and its resulting value.

A Promise mainly solves:

  1. The problem of callback hell
  2. Makes asynchronous code more readable and maintainable
  3. Provides a unified error handling mechanism

A Promise has three states:

Once the state changes from pending to fulfilled or rejected, it becomes immutable (cannot be changed again).

 

面试加分点:A Promise is essentially a state machine, and its callbacks (then/catch) are executed in the microtask queue.

Question 2(基础 + 原理)

中文: Promise 的 then 方法返回的是什么?为什么可以链式调用?

English: What does the then method return, and why can Promises be chained?

 

标准答案(中文)

then 方法一定会返回一个新的 Promise 对象

之所以可以链式调用,是因为:

具体规则:

  1. 如果 then 里返回的是一个普通值: → 会被包装成 Promise.resolve(value)
  2. 如果返回的是一个 Promise: → 下一个 then 会等待这个 Promise 结果
  3. 如果抛出错误: → 会变成 Promise.reject(error),进入 catch

面试加分点:

then 的链式调用本质是“值的穿透 + Promise 的扁平化(flattening)”。

 

Standard Answer (English)

The then method always returns a new Promise.

Chaining works because:

Rules:

  1. If a normal value is returned: → It becomes Promise.resolve(value)
  2. If a Promise is returned: → The next then waits for it
  3. If an error is thrown: → It becomes Promise.reject(error) and goes to catch

面试加分点:

Promise chaining is essentially value propagation + Promise flattening.

 

举个面试常用例子

你可以这样解释:

中文:

English:

 

Question 3(进阶 / 高频)

中文: then 的两个参数(onFulfilled, onRejected)和 catch 有什么区别?为什么通常不推荐在 then 里写第二个参数?

English: What is the difference between the two arguments of then (onFulfilled, onRejected) and catch? Why is it usually not recommended to use the second argument of then?

参考:https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Async_JS/Promises#catching_errors

分析:

推荐写法:


中文回答: then 的两个参数(onFulfilled, onRejected)和 catch 有什么区别?

then(onFulfilled, onRejected) 和 .catch(onRejected) 功能相关,但有重要区别:

最关键的区别在于错误捕获范围:

推荐写法: 成功用 .then(),错误统一用 .catch(),代码更清晰,可读性更好。

 

中文回答:为什么不推荐用 then 的第二个参数?

不推荐使用 then 的第二个参数,因为它:

  1. 不能捕获 then 内部抛出的错误
  2. 会导致错误处理逻辑分散
  3. 不符合链式错误处理的设计(error bubbling)

推荐统一使用 catch 来处理错误,使代码更清晰、可维护。

 

面试加分: Promise 的错误是“冒泡”的,应该在链的末尾统一用 catch 处理。


Standard Answer (English)

then(onFulfilled, onRejected) and .catch(onRejected) are related but have key differences:

The most important difference is error handling scope:

Best practice: Use .then() only for success, and chain .catch() for error handling. It improves readability and reduces bugs.

 

Why not use the second argument of then?

English:

It is not recommended because:

  1. It cannot catch errors thrown inside then
  2. It leads to scattered error handling
  3. It breaks the error propagation chain

Using catch provides a more consistent and maintainable error handling pattern.

 

面试加分:

Errors in Promises bubble down the chain and should be handled at the end using catch.

Question 4(重要)

中文: 如果在 then 里不写 return,会发生什么?值是怎么传递的?

English: What happens if you don’t return anything inside a then? How is the value propagated?

 

标准答案(中文)

如果在 then 里面不写 return,那么:

👉 默认会返回 undefined 👉 等价于:return undefined

并且这个 undefined 会被自动包装成:

然后传递给下一个 then

 

面试加分:

then 的返回值决定了下一个 then 的输入,如果不写 return,默认就是 undefined。


Standard Answer (English)

If you don’t return anything inside a then:

👉 It implicitly returns undefined 👉 Equivalent to: return undefined

This undefined is wrapped as:

and passed to the next then.

 

面试加分:

The return value of then determines the input of the next then. If omitted, it defaults to undefined.


几个例子,看返回值是什么?

例子1:

输出是什么?

为什么?

中文:

English:

例子2:

等价于:

返回了123

 

例子3:

⚠️ 注意!!!

这里没有 return,所以下一个 then 仍然拿到 undefined

 

 

Question 5(核心机制)

中文: Promise.resolve() 和 new Promise(resolve => resolve()) 有什么区别?

English: What is the difference between Promise.resolve() and new Promise(resolve => resolve())?

 

这一题已经进入Promise + 事件循环(event loop)核心考点了。

 

标准答案(中文)

Promise.resolve()new Promise(resolve => resolve()) 都会返回一个已解决(fulfilled)的 Promise,但它们在执行时机行为细节上是有区别的。

执行时机:

行为细节:

Promise.resolve自动“展开”(flatten)Promise


Standard Answer (English)

Both Promise.resolve() and new Promise(resolve => resolve()) return a fulfilled Promise, but they differ in execution timing and behavior details.

execution timing:

behavior details:

Promise.resolve will unwrap nested Promises (flattening)


分析:

1、执行时机:

输出顺序:

为什么?

中文解释:

执行顺序:

  1. 同步代码先执行 → start
  2. 执行 new Promise → 重点:因为里面是同步 → 所以打印 new Promise
  3. resolve 后,把 then 放入微任务队列
  4. 执行 end
  5. 最后执行微任务 → then

English explanation:

  1. Run sync code → start
  2. Execute new Promise → executor runs immediately → new Promise
  3. then callback goes to microtask queue
  4. Run end
  5. Run microtask → then

2、行为细节

输出:1

 

Question 6(事件循环 / 高频)

中文: 下面代码的输出顺序是什么?请解释原因。

English: What is the output order of the following code? Explain why.

 

我的答案是:1,3,4,2。因为我知道setTimeout绝对是延时执行的,即使延迟时间是0。但是最终答案是1,4,3,2。

分析:

👉 同步代码,直接执行 输出:1


👉 宏任务(macrotask),放入任务队列(task queue) (不会立刻执行)


👉 微任务(microtask),放入微任务队列(microtask queue)


👉 同步代码,直接执行 输出:4


到这里为止(同步执行结束)

当前输出:


接下来执行队列

规则(面试必背):

中文: 每一轮事件循环: 👉 先清空所有微任务 👉 再执行一个宏任务


执行微任务:

输出:


再执行宏任务(setTimeout):

输出:


最终结果

 

英文 Explanation:

  1. Synchronous code runs first:

    • 1
    • 4
  2. Then microtasks:

    • 3 (from Promise)
  3. Then macrotasks:

    • 2 (from setTimeout)

Promise 的 then 属于微任务,会在当前同步代码执行完之后、下一个宏任务之前执行。

Promise callbacks (then) are microtasks, executed after synchronous code but before macrotasks.


Key Rule:

Microtasks always run before macrotasks

 

 

Question 7(执行顺序 / 进阶)

中文: 下面代码输出什么?

English: What is the output?

 

我的答案:start, end, promise1, promise2, timeout。回答正确。

分析:

1、第一阶段:同步代码

输出:


👉 宏任务,进入 task queue(暂不执行)


👉 这里很关键:

⚠️ 为什么? 因为第二个 then 要等第一个执行完才会加入队列


输出:


2、第二阶段:执行微任务

当前微任务队列:

执行:

👉 执行完后:


继续执行微任务:


3、第三阶段:执行宏任务


核心考点

关键点 1

then 是“链式微任务”

中文: 后一个 then 要等前一个执行完才会进入微任务队列

English: Each then in a chain is scheduled only after the previous one resolves


关键点 2

微任务队列是“清空执行”的

中文: 一旦开始执行微任务,会一直执行直到队列清空

English: The microtask queue is fully drained before moving to macrotasks


面试加分句:

中文: Promise 的 then 是按链式依赖逐个进入微任务队列,而不是一次性全部进入。

English: Promise then callbacks are queued sequentially based on resolution, not all at once.

 

 

Question 8(执行顺序 / 困难)

中文: 下面代码输出什么?

English: What is the output?

 

我的答案:A, B ,C。正确答案是A, C, B。

关键点是:多个 Promise 链之间的微任务是“交替执行”的,不是一个链跑完再跑另一个。

分析:

第一步:同步阶段

所有 Promise 创建完成


第二步:初始化微任务队列

此时微任务队列是:


第三步:执行第一个微任务(A)

输出:


⚠️ 关键点来了!!!

这里返回了:

👉 所以:


此时队列变成:


第四步:执行 thenC

输出:


第五步:执行 then(B)

输出:

 

Key idea:

Microtasks from different Promise chains are interleaved, not executed chain-by-chain.

中文: 当 then 返回一个 Promise 时,后续 then 会被“延迟”,让其他微任务有机会先执行。

English: When a then returns a Promise, the next then is deferred, allowing other microtasks to run first.

 

 

Question 9(执行顺序 / 难 / 综合)

中文: 下面代码输出什么?

English: What is the output?

 

我的答案:start, end, promise2, timeout1, promise1, timeout2。回答正确。

分析,这题涉及:

1、第一轮(主线程 / 主宏任务)

执行同步代码:

当前输出:

当前队列状态:


清空微任务队列

执行:

输出:


同时执行:

👉 加入宏任务队列


此时队列:


2、第二轮(执行宏任务 timeout1)

输出:


timeout1 内部:

👉 加入微任务队列


清空微任务队列

执行:


3、第三轮(执行宏任务 timeout2)

 

Key points:

  1. 微任务优先级高于宏任务

Microtasks run before macrotasks


  1. 每一轮宏任务执行完都会清空微任务

Each macrotask is followed by draining all microtasks


  1. 宏任务是按顺序进入队列的(FIFO)

Macrotasks are queued in order

 

中文: 事件循环是按“宏任务 → 清空微任务 → 下一个宏任务”的顺序执行的,微任务可以在执行过程中不断插入。

English: The event loop runs as: macrotask → drain microtasks → next macrotask. Microtasks can be dynamically added during execution.

 

Question 10(本质)

中文: async/await 和 Promise 的关系是什么?本质上是怎么实现的?

English: What is the relationship between async/await and Promise? How does it work under the hood?

 

我的回答:async/await 是 promise的语法糖,async/await可以使用同步的方式来写异步代码。怎么实现不知道。

下面看答案:

标准答案(中文)

async/await 本质上是 Promise 的语法糖,它让我们可以用同步的写法来表达异步逻辑

在底层实现上:

  1. async 函数一定返回一个 Promise
  2. await 会把后面的表达式转成 Promise(相当于 Promise.resolve()
  3. await暂停函数执行,把后面的代码放入微任务队列
  4. 等 Promise resolve 后,再恢复执行(类似 .then

Standard Answer (English)

async/await is essentially syntactic sugar over Promises, allowing asynchronous code to be written in a synchronous style.

Under the hood:

  1. An async function always returns a Promise
  2. await converts the expression into a Promise (like Promise.resolve())
  3. await pauses execution and schedules the rest as a microtask
  4. Once resolved, execution resumes (similar to .then)

 

简化的答案:

中文:

async/await 是 Promise 的语法糖,async 函数返回 Promise,await 会把表达式转为 Promise 并暂停执行,把后续代码放入微任务队列,本质上等价于 Promise.then 的链式调用。

English:

Async/await is syntactic sugar over Promises. Async functions return Promises, and await converts expressions into Promises and resumes execution via microtasks, essentially behaving like chained .then calls.


分析:

async/await 写法:

本质等价于:


深度解析:

await 做了什么?

本质可以理解为:

 

关键机制(非常重要)

为什么说它“暂停”?

中文:

English:


常见追问:

Q:await 后面如果不是 Promise 会怎样?

👉 等价于:

 

Q:async 函数 return 什么?

👉 实际返回:

 

 

Question 11(Promise.all)

中文: 请你实现一个 Promise.all,并说明它的行为。

English: Implement Promise.all and explain how it works.

 

常见题目要求(面试官常说的话):

需要满足:

  1. 所有 Promise 成功 → 返回结果数组(按顺序)
  2. 有一个失败 → 立即 reject
  3. 支持普通值(不是 Promise)
  4. 结果顺序必须和输入一致(重点)

分析:

英文版:

 

简单回答:

“手写 Promise.all 的核心难点有两个:一是用计数器判断所有完成,二是必须按索引存放结果来保证顺序。我会用 Promise.resolve() 来兼容普通值,同时任何一个 reject 都会立刻终止。”

The core idea of Promise.all:

  1. Use an array to store results in order
  2. Use a counter to track completion
  3. Use Promise.resolve to normalize inputs
  4. Resolve when all succeed
  5. Reject immediately on any failure
  6. Handle empty array case

 

代码:

 

 

Question 12(Promise.race)

中文

请你实现一个 Promise.race

English

Implement Promise.race

 

题目要求:

需要满足:

  1. 谁先完成(resolve 或 reject)就返回谁
  2. 不关心顺序
  3. 只要有一个 settle(成功或失败),就结束
  4. 支持普通值(非 Promise)
  5. 空数组 → 永远 pending(⚠️ 很多人不知道)

举个例子

👉 输出:2


分析:

race:赛跑、比赛。那么这个方法的意思就是,谁先完成,就返回谁。

那么参考Promise.all的实现方法,这个其实更简单,因为只需要返回一个settle结果,并且不关心顺序,空数组永远pending(意味着不需要考虑空数组返回什么,因为此时的promise.forEach是无法执行的,外层的promise就会一直pending),直接在then里面resolve并且catch里面reject即可。

中文

Promise.race 的核心是:

  1. 遍历所有输入
  2. 用 Promise.resolve 统一处理
  3. 谁先 resolve 或 reject,就立即结束
  4. 不需要计数或顺序控制
  5. 空数组会返回一个 pending Promise

English

The core idea of Promise.race:

  1. Iterate through all inputs
  2. Normalize using Promise.resolve
  3. Resolve or reject as soon as one settles
  4. No need for ordering or counting
  5. Empty array results in a pending Promise

我的答案:


可以优化的点:

⚠️ 点 1:空数组情况(高频考点)

正确行为:永远 pending(不会 resolve / reject)。我的答案是正确的,但是需要主动进行说明让面试官了解。

中文: 如果传入空数组,race 会返回一个永远 pending 的 Promise。因为promises.forEach不会执行。

English: If an empty array is passed, the returned Promise will remain pending forever.

 

⚠️ 点 2:可以简化写法(加分)

你现在写的是:

面试中可以写成更简洁:

 

⚠️ 点 3:变量命名(小优化)

_ 没必要,可以去掉

 

所以最终答案可以是这样的:

 

Question 13 (用Promise封装一个超时功能)

中文解释: 给任意一个 Promise 添加超时时间,如果超过指定时间还未完成,则 reject。

English Explanation: Add a timeout to any promise. If it doesn't settle within the given time, reject with a timeout error.

面试回答模板:

“我常用 Promise.race() 来实现超时功能。一个是正常 Promise,另一个是 setTimeout 的 reject Promise,哪个先完成就用哪个。这样可以有效防止接口长时间无响应。”

Question 14 (用Promise封装一个并发限制函数)

中文解释: 实现一个函数,限制同时运行的异步任务数量(例如最多同时 3 个请求),常用于防止请求过多导致服务器压力过大,避免瞬间撑爆服务器或浏览器带宽。。比如说用户上传图片、文件的时候,可以同时选中很多个文件一起上传,此时使用并发限制函数是很有效的。

English Explanation: Create a function that limits the maximum number of concurrent async tasks (e.g. max 3 requests at the same time).

 

 

为什么要使用Promise.resolve().then(() => task()),这种写法。不能使用new Promise这种写法吗?

 

这两种写法在功能上其实都能达到目的,但使用 Promise.resolve().then(() => task()) 是一种更稳健、简洁的工业级写法。

以下是它们的三大核心区别:

  1. 自动处理“非 Promise”返回值

在你的 tasks 数组里,用户可能不小心传了一个普通函数(不返回 Promise 的函数)。

  1. 异常捕获更优雅

这是最关键的原因。

  1. 语义更清晰

如果你非要用 new Promise,它长这样:

为了达到同样的效果,你需要写得更复杂:

总结

使用 Promise.resolve().then(() => task()) 是一种 “懒人写法”,它利用了 Promise 的内部机制自动处理了:

  1. 同步错误捕获
  2. 返回值 Promise 化

 

为什么我很少自己创建promise呢?比如说我在nextjs项目中,写prisma数据库操作,我会直接使用async await,其余的地方也很少自己创建promise,是不是我能力不够

可以这样说,但这是工作造成的,如果工作中会用到promise,我想我会很快掌握的。

但是面试中对于promise的考察还是很有深度啊,我该怎么办?我主要是做项目,做工具、做框架都很少

掌握好这些面试题,掌握经典的promise用法即可。

promise里面如果嵌套promise的话,还是会造成回调地狱吧?

你说得非常对!如果你在 Promise 的 .then() 里面又去 new Promise() 并且在里面嵌套写 .then(),那确实会演变成“Promise 地狱”,代码看起来和以前的回调嵌套没区别。

但 Promise 设计的精髓在于“链式调用(Chaining)”,它能把嵌套结构拍平

  1. 错误示范:Promise 嵌套(地狱再现)

这种写法虽然用了 Promise,但思维还是回调函数的思维:

  1. 正确示范:链式调用(拍平逻辑)

Promise 规定:.then() 方法里的回调函数,如果返回的是一个新的 Promise,那么下一个 .then() 会等待这个 Promise 完成后再执行。

  1. 终极方案:Async/Await(看起来像同步)

为了彻底解决这种视觉上的“链条感”,ES2017 引入了 async/await。它本质上还是 Promise,但它让异步代码写起来和同步代码一模一样:

总结

我一直在写项目,promise在哪些场景里面可以应用呢?

其实我在很多地方用到了promise,比如说axios, prisma, promise.all(), fetch。使用起来我还是会的,但是我从来没有主动封装过promise相关的工具,那么可以从哪些场景提升自己应用promise的能力呢?

当你发现你正在写的逻辑里出现了 “监听某个事件(如 onSuccess, onComplete)” 或者是 “在回调函数里套回调函数” 的时候,就是你该主动封装 Promise 的信号。