中文: 请你解释一下什么是 Promise?它主要解决了什么问题?
English: Can you explain what a Promise is and what problem it is designed to solve?
标准答案(中文)
Promise 是一个用于表示异步操作结果的对象,它本质是一个状态机 + 回调容器。 它代表一个未来会完成(或失败)的操作结果。
Promise 主要解决的是:
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:
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.
中文: Promise 的 then 方法返回的是什么?为什么可以链式调用?
English:
What does the then method return, and why can Promises be chained?
标准答案(中文)
then 方法一定会返回一个新的 Promise 对象。
之所以可以链式调用,是因为:
then 都会返回一个新的 Promisethen 会接收上一个 Promise 的结果具体规则:
then 里返回的是一个普通值:
→ 会被包装成 Promise.resolve(value)then 会等待这个 Promise 结果Promise.reject(error),进入 catch面试加分点:
then 的链式调用本质是“值的穿透 + Promise 的扁平化(flattening)”。
Standard Answer (English)
The then method always returns a new Promise.
Chaining works because:
then call returns a new Promisethen receives the result of the previous oneRules:
Promise.resolve(value)then waits for itPromise.reject(error) and goes to catch面试加分点:
Promise chaining is essentially value propagation + Promise flattening.
举个面试常用例子
xxxxxxxxxx111Promise.resolve(1)2 .then(res => res + 1)3 .then(res => {4 return Promise.resolve(res + 1);5 })6 .then(res => {7 throw new Error("error");8 })9 .catch(err => {10 console.log(err);11 });你可以这样解释:
中文:
English:
中文:
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?
分析:
xxxxxxxxxx161// 示例1:then 的第二个参数无法捕获前面 onFulfilled 的错误2Promise.resolve()3 .then(() => {4 throw new Error('在成功回调里出错啦'); // ← 这里抛错5 }, (err) => {6 console.log('onRejected:', err); // ← 不会执行!7 });89// 示例2:catch 可以捕获10Promise.resolve()11 .then(() => {12 throw new Error('在成功回调里出错啦');13 })14 .catch((err) => {15 console.log('catch 捕获到:', err); // ← 可以捕获16 });推荐写法:
xxxxxxxxxx111// 推荐:分离成功和失败,代码更清晰2promise3 .then((value) => {4 // 成功处理5 })6 .catch((err) => {7 // 失败处理8 })9 .finally(() => {10 // 无论成功失败都执行11 });中文回答: then 的两个参数(onFulfilled, onRejected)和 catch 有什么区别?
then(onFulfilled, onRejected) 和 .catch(onRejected) 功能相关,但有重要区别:
最关键的区别在于错误捕获范围:
推荐写法: 成功用 .then(),错误统一用 .catch(),代码更清晰,可读性更好。
中文回答:为什么不推荐用 then 的第二个参数?
不推荐使用 then 的第二个参数,因为它:
then 内部抛出的错误推荐统一使用 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:
thenUsing 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.
中文: 如果在 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 会被自动包装成:
xxxxxxxxxx11Promise.resolve(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:
xxxxxxxxxx11Promise.resolve(undefined)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:
xxxxxxxxxx71Promise.resolve(1)2 .then(res => {3 console.log(res); // 14 })5 .then(res => {6 console.log(res); // ?7 });输出是什么?
xxxxxxxxxx2112undefined
为什么?
中文:
then 没有 returnundefinedthen 接收到的就是 undefinedEnglish:
then does not return anythingundefinedthen receives undefined例子2:
xxxxxxxxxx11.then(() => 123)等价于:
xxxxxxxxxx31.then(() => {2 return 123;3})返回了123
例子3:
xxxxxxxxxx31.then(() => {2 Promise.resolve(123);3})⚠️ 注意!!!
这里没有 return,所以下一个 then 仍然拿到 undefined。
中文: 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,但它们在执行时机和行为细节上是有区别的。
执行时机:
new Promise 里的函数是同步执行的Promise.resolve() 是直接创建一个已完成的 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:
new Promise runs synchronouslyPromise.resolve() directly creates a resolved Promisebehavior details:
Promise.resolve will unwrap nested Promises (flattening)
分析:
1、执行时机:
xxxxxxxxxx101console.log("start");23new Promise(resolve => {4 console.log("new Promise");5 resolve();6}).then(() => {7 console.log("then");8});910console.log("end");输出顺序:
xxxxxxxxxx41start2new Promise3end4then
为什么?
中文解释:
执行顺序:
startnew Promise → 重点:因为里面是同步 → 所以打印 new Promisethen 放入微任务队列endthenEnglish explanation:
startnew Promise → executor runs immediately → new Promisethen callback goes to microtask queueendthen2、行为细节
xxxxxxxxxx21Promise.resolve(Promise.resolve(1))2 .then(res => console.log(res));输出:1
中文: 下面代码的输出顺序是什么?请解释原因。
English: What is the output order of the following code? Explain why.
xxxxxxxxxx111console.log("1");23setTimeout(() => {4 console.log("2");5}, 0);67Promise.resolve().then(() => {8 console.log("3");9});1011console.log("4");
我的答案是:1,3,4,2。因为我知道setTimeout绝对是延时执行的,即使延迟时间是0。但是最终答案是1,4,3,2。
分析:
xxxxxxxxxx11console.log("1");👉 同步代码,直接执行
输出:1
xxxxxxxxxx31setTimeout(() => {2 console.log("2");3}, 0);👉 宏任务(macrotask),放入任务队列(task queue) (不会立刻执行)
xxxxxxxxxx31Promise.resolve().then(() => {2 console.log("3");3});👉 微任务(microtask),放入微任务队列(microtask queue)
xxxxxxxxxx11console.log("4");👉 同步代码,直接执行
输出:4
到这里为止(同步执行结束)
当前输出:
xxxxxxxxxx21124
接下来执行队列
规则(面试必背):
中文: 每一轮事件循环: 👉 先清空所有微任务 👉 再执行一个宏任务
执行微任务:
输出:
xxxxxxxxxx113
再执行宏任务(setTimeout):
输出:
xxxxxxxxxx112
最终结果
xxxxxxxxxx411243342
英文 Explanation:
Synchronous code runs first:
14Then microtasks:
3 (from Promise)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
中文: 下面代码输出什么?
English: What is the output?
xxxxxxxxxx151console.log("start");23setTimeout(() => {4 console.log("timeout");5}, 0);67Promise.resolve()8 .then(() => {9 console.log("promise1");10 })11 .then(() => {12 console.log("promise2");13 });1415console.log("end");
我的答案:start, end, promise1, promise2, timeout。回答正确。
分析:
1、第一阶段:同步代码
xxxxxxxxxx11console.log("start");输出:
xxxxxxxxxx11start
xxxxxxxxxx31setTimeout(() => {2 console.log("timeout");3}, 0);👉 宏任务,进入 task queue(暂不执行)
xxxxxxxxxx71Promise.resolve()2 .then(() => {3 console.log("promise1");4 })5 .then(() => {6 console.log("promise2");7 });👉 这里很关键:
then → 放入微任务队列then → 不会立即进入队列⚠️ 为什么?
因为第二个 then 要等第一个执行完才会加入队列
xxxxxxxxxx11console.log("end");输出:
xxxxxxxxxx11end
2、第二阶段:执行微任务
当前微任务队列:
xxxxxxxxxx11promise1
执行:
xxxxxxxxxx11promise1
👉 执行完后:
then 才被加入微任务队列继续执行微任务:
xxxxxxxxxx11promise2
3、第三阶段:执行宏任务
xxxxxxxxxx11timeout
核心考点
关键点 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.
中文: 下面代码输出什么?
English: What is the output?
xxxxxxxxxx131Promise.resolve()2 .then(() => {3 console.log("A");4 return Promise.resolve("B");5 })6 .then(res => {7 console.log(res);8 });910Promise.resolve()11 .then(() => {12 console.log("C");13 });
我的答案:A, B ,C。正确答案是A, C, B。
关键点是:多个 Promise 链之间的微任务是“交替执行”的,不是一个链跑完再跑另一个。
分析:
第一步:同步阶段
所有 Promise 创建完成
第二步:初始化微任务队列
此时微任务队列是:
xxxxxxxxxx21then1(A)2thenC
第三步:执行第一个微任务(A)
xxxxxxxxxx21console.log("A");2return Promise.resolve("B");
输出:
xxxxxxxxxx11A
⚠️ 关键点来了!!!
这里返回了:
xxxxxxxxxx11Promise.resolve("B")
👉 所以:
此时队列变成:
xxxxxxxxxx21thenC2then(B)
第四步:执行 thenC
xxxxxxxxxx11console.log("C");
输出:
xxxxxxxxxx11C
第五步:执行 then(B)
xxxxxxxxxx11console.log("B");
输出:
xxxxxxxxxx11B
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.
中文: 下面代码输出什么?
English: What is the output?
xxxxxxxxxx191console.log("start");23setTimeout(() => {4 console.log("timeout1");56 Promise.resolve().then(() => {7 console.log("promise1");8 });9}, 0);1011Promise.resolve().then(() => {12 console.log("promise2");1314 setTimeout(() => {15 console.log("timeout2");16 }, 0);17});1819console.log("end");
我的答案:start, end, promise2, timeout1, promise1, timeout2。回答正确。
分析,这题涉及:
1、第一轮(主线程 / 主宏任务)
执行同步代码:
xxxxxxxxxx41console.log("start"); // start2setTimeout() // timeout1 → 宏任务队列3Promise.resolve().then() // promise2 → 微任务队列4console.log("end"); // end当前输出:
xxxxxxxxxx21start2end
当前队列状态:
promise2timeout1清空微任务队列
执行:
xxxxxxxxxx11console.log("promise2");
输出:
xxxxxxxxxx11promise2
同时执行:
xxxxxxxxxx31setTimeout(() => {2console.log("timeout2");3}, 0);
👉 加入宏任务队列
此时队列:
timeout1, timeout22、第二轮(执行宏任务 timeout1)
xxxxxxxxxx11console.log("timeout1");
输出:
xxxxxxxxxx11timeout1
timeout1 内部:
xxxxxxxxxx31Promise.resolve().then(() => {2console.log("promise1");3});
👉 加入微任务队列
清空微任务队列
执行:
xxxxxxxxxx11promise1
3、第三轮(执行宏任务 timeout2)
xxxxxxxxxx11timeout2
Key points:
Microtasks run before macrotasks
Each macrotask is followed by draining all microtasks
Macrotasks are queued in order
中文: 事件循环是按“宏任务 → 清空微任务 → 下一个宏任务”的顺序执行的,微任务可以在执行过程中不断插入。
English: The event loop runs as: macrotask → drain microtasks → next macrotask. Microtasks can be dynamically added during execution.
中文: 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 的语法糖,它让我们可以用同步的写法来表达异步逻辑。
在底层实现上:
async 函数一定返回一个 Promiseawait 会把后面的表达式转成 Promise(相当于 Promise.resolve())await 会暂停函数执行,把后面的代码放入微任务队列.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:
async function always returns a Promiseawait converts the expression into a Promise (like Promise.resolve())await pauses execution and schedules the rest as a microtask.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 写法:
xxxxxxxxxx41async function test() {2 const res = await Promise.resolve(1);3 console.log(res);4}本质等价于:
xxxxxxxxxx51function test() {2 return Promise.resolve(1).then(res => {3 console.log(res);4 });5}深度解析:
await 做了什么?
xxxxxxxxxx11await xxx
本质可以理解为:
xxxxxxxxxx31Promise.resolve(xxx).then(result => {2// 后面的代码3});
关键机制(非常重要)
为什么说它“暂停”?
中文:
English:
.then (microtask)常见追问:
Q:await 后面如果不是 Promise 会怎样?
xxxxxxxxxx11await 123👉 等价于:
xxxxxxxxxx11await Promise.resolve(123)
Q:async 函数 return 什么?
xxxxxxxxxx31async function test() {2 return 1;3}👉 实际返回:
xxxxxxxxxx11Promise.resolve(1)
中文:
请你实现一个 Promise.all,并说明它的行为。
English:
Implement Promise.all and explain how it works.
常见题目要求(面试官常说的话):
xxxxxxxxxx11Promise.myAll([p1, p2, p3])
需要满足:
分析:
英文版:
简单回答:
“手写 Promise.all 的核心难点有两个:一是用计数器判断所有完成,二是必须按索引存放结果来保证顺序。我会用 Promise.resolve() 来兼容普通值,同时任何一个 reject 都会立刻终止。”
The core idea of Promise.all:
代码:
xxxxxxxxxx331Promise.myAll = function(promises) {2 return new Promise((resolve, reject) => {3 // 1. Input validation4 if(!Array.isArray(promises) ) {5 return reject(new TypeError("Arguments must be an array"))6 }78 const len = promises.length;9 const results = new Array(len); // pre-allocate to keep order10 let completed = 0;1112 // 这里是重点,如果数组为空,没有加上这句的话,就永远不会resolve13 if(len === 0) {14 return resolve([])15 }1617 promises.forEach((promise, index) => {18 // 3、support ordinary values → use Promise.resolve()19 Promise.resolve(promise).then(value => {20 results[index] = value; // 4、keep original order by using index21 completed++;2223 // all succeeded → resolve with results array24 if(completed === promises.length) {25 resolve(results)26 }27 }).catch(err => {28 // if anyone fails → immediately reject29 reject(err)30 })31 })32 })33}
中文
请你实现一个 Promise.race
English
Implement Promise.race
题目要求:
xxxxxxxxxx11Promise.myRace([p1, p2, p3])需要满足:
举个例子
xxxxxxxxxx41Promise.race([2 new Promise(res => setTimeout(() => res(1), 100)),3 new Promise(res => setTimeout(() => res(2), 50))4]).then(console.log)👉 输出:2
分析:
race:赛跑、比赛。那么这个方法的意思就是,谁先完成,就返回谁。
那么参考Promise.all的实现方法,这个其实更简单,因为只需要返回一个settle结果,并且不关心顺序,空数组永远pending(意味着不需要考虑空数组返回什么,因为此时的promise.forEach是无法执行的,外层的promise就会一直pending),直接在then里面resolve并且catch里面reject即可。
中文
Promise.race 的核心是:
English
The core idea of Promise.race:
我的答案:
xxxxxxxxxx131Promise.myRace = function(promises) {2 return new Promise((resolve, reject) => {3 if(!Array.isArray(promises)) {4 return reject(new TypeError("Arguments must be an array"))5 }67 promises.forEach((promise, _) => {8 Promise.resolve(promise).then(value => {9 resolve(value)10 }).catch(reject)11 })12 })13}可以优化的点:
⚠️ 点 1:空数组情况(高频考点)
xxxxxxxxxx11Promise.race([])
正确行为:永远 pending(不会 resolve / reject)。我的答案是正确的,但是需要主动进行说明让面试官了解。
中文: 如果传入空数组,race 会返回一个永远 pending 的 Promise。因为promises.forEach不会执行。
English: If an empty array is passed, the returned Promise will remain pending forever.
⚠️ 点 2:可以简化写法(加分)
你现在写的是:
xxxxxxxxxx21.then(value => resolve(value))2.catch(reject)面试中可以写成更简洁:
xxxxxxxxxx11.then(resolve, reject)
⚠️ 点 3:变量命名(小优化)
xxxxxxxxxx11(promises, _)_ 没必要,可以去掉
所以最终答案可以是这样的:
xxxxxxxxxx111Promise.myRace = function(promises) {2 return new Promise((resolve, reject) => {3 if (!Array.isArray(promises)) {4 return reject(new TypeError("Arguments must be an array"));5 }67 promises.forEach(promise => {8 Promise.resolve(promise).then(resolve, reject);9 });10 });11};
中文解释: 给任意一个 Promise 添加超时时间,如果超过指定时间还未完成,则 reject。
English Explanation: Add a timeout to any promise. If it doesn't settle within the given time, reject with a timeout error.
xxxxxxxxxx151function withTimeout(promise, ms) {2 const timeout = new Promise((resolve, reject) = >{3 setTimeout(() => {4 reject(new Error(`Promise 超时:${ms}ms`))5 }, ms)6 })78 return Promise.race([promise, timeout])9}1011// 使用12async function fetchData() {13 const data = await withTimeout(fetch('/api/data'), 5000)14 return data;15}面试回答模板:
“我常用 Promise.race() 来实现超时功能。一个是正常 Promise,另一个是 setTimeout 的 reject 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).
xxxxxxxxxx281async function limitConcurrency(tasks, limit = 3) {2 const results = []; // 存储所有任务的 Promise3 const executing = []; // 存储正在执行的任务池45 for (const task of tasks) {6 // 1. 将任务包装成 Promise,并立即开始执行7 const p = Promise.resolve().then(() => task());8 results.push(p);910 // 2. 将正在执行的任务加入池子11 executing.push(p);1213 // 3. 关键:任务完成后,自动从池子中移除自己14 // 无论成功或失败 (finally),都要腾出位置15 p.finally(() => {16 const index = executing.indexOf(p);17 if (index > -1) executing.splice(index, 1);18 });1920 // 4. 如果池子满了,就等待“最快的一个”任务完成,腾出空位21 if (executing.length >= limit) {22 await Promise.race(executing);23 }24 }2526 // 5. 当所有任务都发起了,最后等待全部任务的结果27 return Promise.all(results);28}
为什么要使用Promise.resolve().then(() => task()),这种写法。不能使用new Promise这种写法吗?
这两种写法在功能上其实都能达到目的,但使用
Promise.resolve().then(() => task())是一种更稳健、简洁的工业级写法。以下是它们的三大核心区别:
- 自动处理“非 Promise”返回值
在你的
tasks数组里,用户可能不小心传了一个普通函数(不返回 Promise 的函数)。
- 使用
Promise.resolve().then(...):它会自动将task()的返回值包裹成 Promise。即使task只是() => 123,后续的p.finally也能正常工作。- 使用
new Promise:你需要手动在里面写resolve(task())。
- 异常捕获更优雅
这是最关键的原因。
Promise.resolve().then(() => task()):如果task()函数在执行瞬间报错(同步异常),这个错误会被.then自动捕获并转化为一个rejected状态的 Promise。
new Promisexxxxxxxxxx91// 如果这样写,如果 task() 执行时报错,由于它在 executor 同步代码里2// 你必须手动写 try...catch 才能捕获3const p = new Promise((resolve, reject) => {4try {5resolve(task());6} catch(e) {7reject(e);8}9});而
xxxxxxxxxx11Promise.resolve().then帮你把这些繁琐的
xxxxxxxxxx11try...catch都省掉了。
- 语义更清晰
new Promise通常用于封装一个原本不是 Promise 的东西(比如setTimeout)。Promise.resolve().then通常用于启动一个已有的异步逻辑,或者将一系列逻辑链条化。在调度器(Limit Concurrency)场景下,我们是在“启动”任务,而不是在“封装”底层 API,所以这种写法更符合语义。如果你非要用
new Promise,它长这样:为了达到同样的效果,你需要写得更复杂:
xxxxxxxxxx101const p = new Promise((resolve, reject) => {2// 需要手动立即执行 task3try {4const result = task();5// 还需要手动判断 result 是不是 Promise 并处理6Promise.resolve(result).then(resolve, reject);7} catch (e) {8reject(e);9}10});总结
使用
Promise.resolve().then(() => task())是一种 “懒人写法”,它利用了 Promise 的内部机制自动处理了:
- 同步错误捕获。
- 返回值 Promise 化。
可以这样说,但这是工作造成的,如果工作中会用到promise,我想我会很快掌握的。
掌握好这些面试题,掌握经典的promise用法即可。
你说得非常对!如果你在 Promise 的 .then() 里面又去 new Promise() 并且在里面嵌套写 .then(),那确实会演变成“Promise 地狱”,代码看起来和以前的回调嵌套没区别。
但 Promise 设计的精髓在于“链式调用(Chaining)”,它能把嵌套结构拍平。
这种写法虽然用了 Promise,但思维还是回调函数的思维:
xxxxxxxxxx81// ❌ 错误示范:嵌套2api.getUser().then(user => {3 api.getOrders(user.id).then(orders => {4 api.getOrderDetail(orders[0].id).then(detail => {5 console.log(detail);6 });7 });8});Promise 规定:.then() 方法里的回调函数,如果返回的是一个新的 Promise,那么下一个 .then() 会等待这个 Promise 完成后再执行。
xxxxxxxxxx181// ✅ 正确示范:链式调用2api.getUser()3 .then(user => {4 // 返回一个 Promise5 return api.getOrders(user.id); 6 })7 .then(orders => {8 // 这里拿到的是上面 getOrders 的结果9 return api.getOrderDetail(orders[0].id);10 })11 .then(detail => {12 // 这里拿到的是 detail13 console.log(detail);14 })15 .catch(err => {16 // 整个链条上任何一步出错,都会在这里被捕获17 console.error(err);18 });为了彻底解决这种视觉上的“链条感”,ES2017 引入了 async/await。它本质上还是 Promise,但它让异步代码写起来和同步代码一模一样:
xxxxxxxxxx111// 🚀 终极方案:Async/Await2async function getInfo() {3 try {4 const user = await api.getUser();5 const orders = await api.getOrders(user.id);6 const detail = await api.getOrderDetail(orders[0].id);7 console.log(detail);8 } catch (err) {9 console.error(err);10 }11}总结
return 将逻辑拍平。其实我在很多地方用到了promise,比如说axios, prisma, promise.all(), fetch。使用起来我还是会的,但是我从来没有主动封装过promise相关的工具,那么可以从哪些场景提升自己应用promise的能力呢?
当你发现你正在写的逻辑里出现了 “监听某个事件(如 onSuccess, onComplete)” 或者是 “在回调函数里套回调函数” 的时候,就是你该主动封装 Promise 的信号。