前端5月30日 10:11
Promise.all 和 Promise.race 有什么区别?`Promise.all` 等“全部成功”,`Promise.race` 等“第一个完成”。`all` 会并行启动所有 Promise,只有全部 fulfilled 才 fulfilled,结果数组顺序和传入顺序一致;任意一个 rejected,整体立刻 rejected。`race` 也是并行启动,但谁先 settled 就采用谁的结果,不管成功还是失败。
## 追问
### all 和 race 的返回值有什么不同?
`all` 返回结果数组;`race` 返回第一个完成的值或错误。
### all 里有一个失败,其他请求会取消吗?
不会。`all` 只是让返回的 Promise 变成 rejected,已经发出去的请求仍会继续,除非额外用 `AbortController` 取消。
### 空数组会怎样?
`Promise.all([])` 立即 fulfilled,值是 `[]`;`Promise.race([])` 会一直 pending。
### allSettled 和 any 什么时候用?
想知道每个任务成败,用 `allSettled`;只要任意一个成功就够,用 `any`。
## 写段代码
```javascript
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), 3000)
);
Promise.race([fetch('/api/data'), timeout]).catch(console.error);
Promise.all([fetch('/a'), fetch('/b')]);
```标签
Promise
Promise 是一种用于延迟计算的策略,适用于多种并发风格:用于本地计算的线程和事件循环并发,以及同步和异步远程消息传递。Promise 代表一个异步操作的最终结果。使用 Promises 的主要方式是通过一个方法,该方法注册从 promise 的最终值或失败原因到新 promise 的转换。

前端5月30日 10:11
Promise 链式调用是怎么工作的?Promise 链式调用的核心是:每次调用 `.then()` 都会返回一个新的 Promise,后一个 `.then()` 接收前一个回调的返回值。返回普通值就直接传下去;返回 Promise 就等待它 settled;抛错或返回 rejected Promise,错误会沿链向后冒泡,直到被 `.catch()` 捕获。
## 追问
### then 里返回普通值和 Promise 有什么区别?
返回普通值时,下一个 then 立即拿到这个值;返回 Promise 时,下一个 then 要等它完成后再执行。
### then 里不 return 会怎样?
等于返回 `undefined`,所以下一个 then 收到的就是 `undefined`。很多链式调用断数据,问题都出在这里。
### catch 后面的 then 还会执行吗?
会。catch 如果返回正常值,链会恢复为 fulfilled;如果继续 throw,后面仍然走 rejected 分支。
### async/await 和链式调用是什么关系?
async/await 本质是 Promise 的语法糖,适合写顺序流程;链式调用适合短管道或函数组合。
## 写段代码
```javascript
Promise.resolve(1)
.then(v => v + 1)
.then(v => Promise.resolve(v * 2))
.then(v => { throw new Error('bad') })
.catch(() => 'fallback')
.then(console.log);
```前端5月30日 02:24
Promise 错误处理面试怎么答?Promise 错误处理要抓住两句话:错误会沿 Promise 链向后传播,最近的 catch 或 then 第二个参数会接住它;catch 返回普通值表示恢复,重新 throw 才会继续失败。项目里一般推荐在链尾统一 catch,async/await 用 try/catch;多个任务用 Promise.allSettled 处理部分失败,避免一个请求失败拖垮全部结果。
## 追问
### catch 和 then 的第二个参数有什么区别?
then 的第二个参数只能处理前一个 Promise 的失败,抓不到同一个 then 成功回调里新抛出的错误;catch 更适合放在链尾统一兜底。
### catch 里 return 和 throw 有什么区别?
return 会把链恢复成 fulfilled,后面的 then 会继续走;throw 或 return rejected Promise 才会让后续 catch 接着处理。
### Promise.all 里一个失败怎么办?
Promise.all 会快速失败,只要一个 reject 整体就 reject。需要拿到每个任务结果时,用 Promise.allSettled,或给每个任务单独 catch。
### 未捕获的 Promise 错误怎么排查?
浏览器看 `unhandledrejection`,Node 看 `unhandledRejection` 日志。根因通常是忘记 await、忘记 return Promise,或 catch 里吞错。
## 写段代码
```js
async function loadAll(tasks) {
const results = await Promise.allSettled(tasks.map(t => t()));
return results.map(r =>
r.status === 'fulfilled' ? r.value : { error: r.reason.message }
);
}
```前端5月30日 02:24
Promise 并发控制如何实现?Promise 并发控制就是限制同一时间运行的异步任务数量。面试可以先说结论:维护一个任务池,启动任务后放入 executing,数量达到上限就 await Promise.race(executing),等任意任务结束再继续放新任务。最后用 Promise.all 收集全部结果。真实项目里常用于批量请求、上传、爬取、发邮件,目的是保护浏览器连接数、服务端限流和内存。
## 追问
### 并发控制和 Promise.all 有什么区别?
Promise.all 会一次性启动所有任务;并发控制只同时跑固定数量。前者适合少量独立任务,后者适合几百、几千个任务。
### 为什么用 Promise.race?
它能等“最先完成的一个任务”。有任务完成后,池子空出位置,就可以继续补下一个任务。
### 失败任务怎么处理?
看业务。要快速失败就让错误抛出;要尽量完成全部任务,就给每个任务包一层 catch,返回 `{status, value, reason}`。
### 并发数怎么定?
没有固定答案。浏览器请求可从 4-8 开始;Node 服务要看下游 QPS、CPU、连接池和超时率,再动态调。
## 写段代码
```js
async function pool(limit, list, worker) {
const ret = [];
const running = [];
for (const item of list) {
const p = Promise.resolve().then(() => worker(item));
ret.push(p);
const e = p.finally(() => running.splice(running.indexOf(e), 1));
running.push(e);
if (running.length >= limit) await Promise.race(running);
}
return Promise.all(ret);
}
```前端5月30日 02:24
Promise 性能优化面试怎么答?Promise 性能优化的核心不是“少用 Promise”,而是少制造没必要的异步层级,让能并行的任务并行,让高频请求有缓存、去重和并发限制。面试里先答三点:避免 new Promise 包一层已有 Promise;独立任务用 Promise.all 并行;批量任务别一次性打满,用队列或 p-limit 控制数量。再补一句:性能问题通常来自请求瀑布、重复请求、长链微任务和未释放的引用。
## 追问
### Promise.all 一定更快吗?
不一定。只有任务互不依赖时才更快;如果后一个请求依赖前一个结果,强行并行会写错逻辑。Promise.all 还会遇到一个失败就整体失败的问题。
### 为什么不建议包一层 new Promise?
已有 Promise 直接返回即可。多包一层会增加对象创建、微任务调度和错误传播复杂度,还容易漏掉 reject。
### 请求去重怎么做?
用 Map 保存进行中的 Promise,相同 key 直接复用;结束后在 finally 里删除,避免内存泄漏。
### 长 Promise 链会慢吗?
真正慢的通常不是链本身,而是链里塞了大量同步计算或无意义 then。可读性差时改 async/await,但不要把独立任务写成串行 await。
## 写段代码
```js
const pending = new Map();
function once(key, fn) {
if (pending.has(key)) return pending.get(key);
const p = fn().finally(() => pending.delete(key));
pending.set(key, p);
return p;
}
async function load() {
const [user, posts] = await Promise.all([
once('user', fetchUser),
once('posts', fetchPosts)
]);
return { user, posts };
}
```服务端5月30日 02:24
Promise.allSettled() 有什么作用?和 Promise.all 有什么区别?`Promise.allSettled()` 会等待一组 Promise 全部结束,不管成功还是失败,最后返回每个任务的状态和结果;`Promise.all()` 则要求全部成功,只要一个 reject 就立刻 reject。面试里一句话区分:all 适合“缺一个都不行”,allSettled 适合“尽量都跑完,再分别处理结果”。例如批量请求、批量上传、页面多个独立模块加载,更适合 allSettled。
## 追问
### allSettled 返回值长什么样?
每一项都有 `status`。成功是 `{ status: 'fulfilled', value }`,失败是 `{ status: 'rejected', reason }`。
### allSettled 会吞掉错误吗?
不会吞,只是把错误包装进结果数组。你仍然要检查 rejected 项,否则失败会被业务层忽略。
### 什么时候不能用 allSettled?
任务之间有强依赖时不适合。比如用户信息失败后,后续请求必须停止,这时用 all 或串行 await 更清楚。
### 和给每个 Promise 单独 catch 有什么区别?
单独 catch 可以兼容更老环境,也能自定义返回结构;allSettled 是标准化写法,语义更明确。
## 写段代码
```javascript
const results = await Promise.allSettled([fetchUser(), fetchPosts()]);
const ok = results.filter(r => r.status === 'fulfilled').map(r => r.value);
const failed = results.filter(r => r.status === 'rejected').map(r => r.reason);
```服务端5月30日 02:24
Promise 和回调函数有什么区别?为什么能解决回调地狱?Promise 和回调函数都能处理异步,但抽象层次不同。回调是“异步完成后调用你给的函数”,容易出现多层嵌套、错误分散、控制流难追踪;Promise 把异步结果封装成一个有状态对象,可以链式调用、统一 catch、组合 `all/race/any/allSettled`。面试里可以说:Promise 不是让异步变同步,而是让异步流程更可组合、错误更集中、代码更容易维护。
## 追问
### Promise 解决了回调地狱吗?
解决了一部分。链式 then 可以拉平嵌套,async/await 又进一步接近同步写法。但如果业务拆分不好,Promise 链也会写得很乱。
### Promise 的错误处理强在哪里?
回调通常要每层传 `err`;Promise 的错误会沿链传播,最后一个 catch 可以兜底处理。
### 回调还有用吗?
有。事件监听、流、Node 风格 API、低层库里仍常见回调。Promise 更适合“一次性成功或失败”的异步结果。
### Promise 有什么代价?
它会引入微任务调度和对象创建,不适合滥用在纯同步逻辑里。性能敏感代码要避免无意义包装。
## 写段代码
```javascript
readFile('a.txt')
.then(parse)
.then(save)
.catch(handleError);
```服务端5月30日 02:24
Promise.any() 有什么作用?和 Promise.race 有什么区别?`Promise.any()` 的作用是:一组 Promise 里只要有一个成功,就立刻返回这个成功结果;只有全部失败时,才会 reject,并抛出 `AggregateError`。它适合“多个候选源,谁先成功用谁”的场景,比如多 CDN 拉资源、多个镜像接口兜底。面试里要强调:它忽略失败,只关心第一个成功;这和 `Promise.race()` 谁先 settled 就返回完全不同。
## 追问
### Promise.any 和 Promise.race 最大区别是什么?
`race` 看第一个完成,不管成功还是失败;`any` 看第一个成功,失败会被暂时忽略,除非全部失败。
### 全部失败时会发生什么?
会 reject 一个 `AggregateError`,里面的 `errors` 保存所有失败原因。不要只 catch 后打印 message,最好把 errors 也记录下来。
### 它和 Promise.all 有什么区别?
`all` 要全部成功才成功,一个失败就失败;`any` 只要一个成功就成功。一个适合“都要”,一个适合“有一个可用就行”。
### 实际项目里怎么用?
适合容灾兜底,不适合支付、写入、下单这类不能重复尝试的操作。并行请求多个源时也要考虑取消慢请求或控制成本。
## 写段代码
```javascript
try {
const data = await Promise.any([
fetch('/cdn-a/config.json'),
fetch('/cdn-b/config.json')
]);
console.log(await data.json());
} catch (e) {
console.error(e.errors);
}
```前端5月27日 23:41
async/await 的执行原理是什么?与 Promise 和事件循环有什么关系?async/await 是 ES2017 引入的异步编程语法,本质上基于 Promise 和 Generator 实现。理解它的工作原理,关键在于弄清 await 做了什么、代码到底在哪一步暂停、以及它与事件循环中微任务队列的关系。
## async 函数的返回值
async 函数无论内部返回什么,调用它拿到的永远是一个 Promise。返回普通值会被 Promise.resolve() 包装,抛出异常则对应一个 rejected 的 Promise。
```javascript
async function foo() {
return 42;
}
foo(); // Promise { fulfilled: 42 }
async function bar() {
throw new Error("fail");
}
bar(); // Promise { rejected: Error: fail }
```
这一点是后续理解执行流程的前提:async 函数本身并不异步执行,函数体内 await 之前的代码是同步运行的,只有遇到 await 才会产生暂停效果。
## await 到底做了什么
await 的执行分两步:
1. 立即求值 await 右侧的表达式。如果右侧不是 Promise,则用 Promise.resolve() 包装。
2. 暂停当前 async 函数的执行,将 await 之后的代码注册为该 Promise 的 then 回调——即放入微任务队列。
注意:await 不会阻塞整个 JavaScript 主线程,它只暂停自己所在的 async 函数。外部调用栈会继续往下执行。
```javascript
async function demo() {
console.log(1);
await Promise.resolve();
console.log(2);
}
console.log("a");
demo();
console.log("b");
// 输出顺序: a → 1 → b → 2
```
为什么是 a → 1 → b → 2?console.log("a") 同步执行;调用 demo() 进入函数体,console.log(1) 同步执行;遇到 await,后面的 console.log(2) 被放入微任务队列,函数返回一个 pending 的 Promise;回到调用栈继续执行 console.log("b");同步代码跑完后,事件循环检查微任务队列,执行 console.log(2)。
## 事件循环与微任务队列
JavaScript 的事件循环模型决定了 async/await 的执行时序:
- 宏任务:script 整体代码、setTimeout、setInterval、I/O 回调等
- 微任务:Promise.then/catch/finally、await 之后的代码、queueMicrotask 等
执行规则:每执行完一个宏任务,就会清空整个微任务队列,然后再执行下一个宏任务。
```javascript
console.log("script start");
setTimeout(() => console.log("setTimeout"), 0);
Promise.resolve()
.then(() => console.log("promise1"))
.then(() => 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
```
解析:同步代码先执行完毕;微任务队列中 promise1 先入队,async1 end 随后入队(因为 await async2() 右侧同步执行完后,await 后的代码才入微任务),所以 promise1 先输出,接着 async1 end,然后 promise1.then 产生 promise2 再执行;最后才轮到宏任务 setTimeout。
## async/await 与 Promise 的等价转换
async/await 是 Promise 的语法糖,每一段 async/await 代码都可以机械地改写为 Promise 链式调用:
```javascript
// async/await 写法
async function fetchUser() {
try {
const res = await fetch("/api/user");
const data = await res.json();
return data;
} catch (e) {
console.error(e);
throw e;
}
}
// 等价 Promise 写法
function fetchUser() {
return fetch("/api/user")
.then(res => res.json())
.then(data => data)
.catch(e => {
console.error(e);
throw e;
});
}
```
V8 引擎在早期版本中将 async/await 编译为基于 Generator 的状态机(配合 __awaiter 辅助函数),现代 V8 已优化为直接生成 Promise 链,减少了 Generator 中间层带来的性能开销。
## 错误处理
### try/catch 捕获
```javascript
async function fetchData() {
try {
const res = await fetch("/api/data");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (e) {
console.error("请求失败:", e.message);
throw e;
}
}
```
### try/catch 能否捕获未 await 的 Promise 异常?
不能。如果忘记 await,异常是 Promise 的 rejection,不会冒泡到外层 try/catch:
```javascript
async function foo() {
try {
asyncFnThatThrows(); // 没有 await,异常丢失
} catch (e) {
// 捕获不到
}
}
```
必须加 await 或手动 .catch() 才能捕获。
## 并发控制
### 顺序 vs 并行
多个独立的异步操作不要用 await 逐个等待,应使用 Promise.all 并行执行:
```javascript
// 顺序执行 — 慢
async function sequential() {
const a = await fetchA(); // 等 1s
const b = await fetchB(); // 再等 1s,总计 ~2s
}
// 并行执行 — 快
async function parallel() {
const [a, b] = await Promise.all([fetchA(), fetchB()]); // 总计 ~1s
}
```
### 容错并行
Promise.allSettled 不会因某个请求失败而中断,适合需要全部结果(含失败)的场景:
```javascript
async function fetchAll() {
const results = await Promise.allSettled([
fetchUser(),
fetchPosts(),
fetchComments()
]);
results.forEach(r => {
if (r.status === "fulfilled") console.log(r.value);
else console.error(r.reason);
});
}
```
## 常见陷阱
### 在循环中顺序 await
```javascript
// 慢 — 逐个等待
for (const url of urls) {
const data = await fetch(url);
process(data);
}
// 快 — 并发请求
const results = await Promise.all(urls.map(u => fetch(u)));
results.forEach(process);
```
### 在顶层直接使用 await
ES2022 引入了 Top-level await,在 ES Module 的顶层可以直接使用 await,但 CommonJS 模块中仍需包裹在 async 函数内。
### await 只能用在 async 函数内
```javascript
function foo() {
await bar(); // SyntaxError
}
```
## 面试追问
Q: async 函数中 await 一个非 Promise 值会怎样?
会自动用 Promise.resolve() 包装,等价于 await 一个立即 resolve 的 Promise。await 之后的代码仍会进入微任务队列,在当前同步代码执行完后才运行。
Q: 为什么 await 后面的代码是微任务而不是宏任务?
因为 await 的语义是等待 Promise 完成后继续执行,这个继续执行本质上就是 Promise 的 then 回调,而 Promise.then 属于微任务。如果放在宏任务队列中,每轮事件循环只会执行一个宏任务,延迟过高且不符合语义。
Q: async/await 相比 Promise.then 链式调用有什么不足?
两个主要局限:一是无法方便地实现 Promise.race/all 等组合逻辑,仍需回到 Promise API;二是 try/catch 无法区分错误来源,而 .catch() 可以在特定 .then 后精准捕获。前端5月27日 23:35
如何实现 Promise 的取消?Promise 一旦创建就无法从外部中断它的执行——这是面试中频繁出现的考点,也是实际开发中经常遇到的痛点。下面直接给出答案,再逐步分析每种方案的原理和取舍。
## 核心答案
Promise 本身不支持取消。状态一旦从 pending 变为 fulfilled 或 rejected 就不可逆,这是规范设计决定的。但我们可以通过以下方式间接实现取消效果:
| 方案 | 原理 | 是否真正取消 | 适用场景 |
|------|------|------------|---------|
| AbortController | 浏览器标准 API,通过 signal 通知异步操作中止 | 是(对支持的 API) | fetch、Node.js 流操作等 |
| 包装函数 | 用标志位忽略 resolve/reject 的结果 | 否,仅忽略结果 | 简单场景、旧代码兼容 |
| CancellationToken | 手动传递令牌,在关键节点检查 | 半取消(需主动配合) | 复杂业务逻辑、多步骤任务 |
| Promise.race | 用超时 Promise 竞争 | 否,仅忽略结果 | 超时控制 |
**面试追问答法**:为什么说包装函数不是真正取消?——因为原始 Promise 内部的异步操作仍在执行,只是我们不再处理它的结果。真正的取消需要异步操作本身支持中止,比如 fetch 接收到 abort 信号后会终止 TCP 连接。
## 为什么 Promise 规范不内置取消?
ES6 Promise 遵循 Promises/A+ 规范,核心设计原则是**不可变性**:状态一旦确定就不再改变。这个设计换来了两个关键保证:
- **可靠性**:then 注册的回调一定会在状态确定后执行,不存在"取消导致回调不执行"的歧义
- **可组合性**:Promise 链可以自由组合,不必担心中间环节被意外取消
取消操作引入的副作用(资源未释放、回调丢失、竞态条件)远大于收益,所以规范层面选择了不支持。Domenic Denicola 曾在 TC39 提案中解释过这个设计决策:取消是**操作**的属性,不是**值**的属性,而 Promise 代表的是值。
## AbortController:标准方案详解
AbortController 是 Web API(不是 ECMAScript 规范),但已成为事实上的取消标准。Node.js 从 v15 起完整支持。
### 基本用法
```javascript
const controller = new AbortController();
const signal = controller.signal;
fetch('/api/data', { signal })
.then(res => res.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('请求已取消');
}
});
// 取消
controller.abort();
```
调用 `abort()` 后,signal 上的 `aborted` 属性变为 true,同时触发 abort 事件。fetch 内部监听了这个信号,会主动断开请求。
### 封装超时请求
```javascript
function fetchWithTimeout(url, options = {}, timeout = 5000) {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
return fetch(url, { ...options, signal: controller.signal })
.then(res => {
clearTimeout(id);
return res.json();
})
.catch(err => {
clearTimeout(id);
if (err.name === 'AbortError') {
throw new Error(`请求超时(${timeout}ms)`);
}
throw err;
});
}
```
### AbortSignal.timeout()——更简洁的超时方案
现代浏览器和 Node.js 18+ 支持 `AbortSignal.timeout()`,无需手动管理 setTimeout:
```javascript
// 5 秒超时自动取消
fetch('/api/data', { signal: AbortSignal.timeout(5000) })
.then(res => res.json())
.catch(err => {
if (err.name === 'AbortError') {
console.log('超时或手动取消');
}
});
```
### 给自定义异步函数添加取消支持
关键是监听 signal 的 abort 事件并在回调中执行清理:
```javascript
async function delay(ms, { signal } = {}) {
return new Promise((resolve, reject) => {
if (signal?.aborted) {
return reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
}
const timer = setTimeout(resolve, ms);
signal?.addEventListener('abort', () => {
clearTimeout(timer);
reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
}, { once: true });
});
}
```
> 注意:必须用 `{ once: true }` 防止重复触发,且要在 Promise resolve 后清除定时器避免资源泄漏。
## 包装函数方案
不依赖任何 API,兼容性最好,但只能"忽略结果",不能"停止执行":
```javascript
function makeCancellable(promise) {
let cancelled = false;
const wrapped = new Promise((resolve, reject) => {
promise.then(
val => cancelled || resolve(val),
err => cancelled || reject(err)
);
});
return {
promise: wrapped,
cancel() { cancelled = true; }
};
}
// 使用
const { promise, cancel } = makeCancellable(
fetch('/api/data').then(r => r.json())
);
promise.then(data => console.log(data));
cancel(); // 后续 then 不会执行,但 fetch 请求仍在进行
```
**面试追问**:这种方案有什么隐患?——即使调用了 cancel,原始请求仍在运行,如果它最终 resolve,回调虽不执行,但占用的网络和内存资源不会释放。对于大量并发请求的场景,这会造成资源浪费。
## CancellationToken 模式
在多步骤任务中,需要在每个关键节点主动检查取消状态:
```javascript
class CancellationToken {
#cancelled = false;
#reason = null;
get isCancelled() { return this.#cancelled; }
get reason() { return this.#reason; }
cancel(reason) {
this.#cancelled = true;
this.#reason = reason ?? 'Operation cancelled';
}
throwIfCancelled() {
if (this.#cancelled) throw new Error(this.#reason);
}
}
// 多步骤任务中使用
async function processOrder(orderId, token) {
token.throwIfCancelled();
const order = await fetchOrder(orderId);
token.throwIfCancelled();
const payment = await processPayment(order);
token.throwIfCancelled();
await confirmOrder(order, payment);
}
```
这种模式需要开发者在代码中主动插入检查点,适合步骤清晰的长任务。缺点是如果某个步骤的 Promise 已提交但还没到下一个检查点,中间这段时间无法响应取消。
## Promise.race 实现超时
```javascript
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms)
);
return Promise.race([promise, timeout]);
}
```
这种方式简洁但有两个问题:一是超时后原始 Promise 仍在执行;二是如果原始 Promise 先 reject,超时定时器不会清理,造成轻微的内存泄漏。实际生产中优先用 AbortController。
## 实战:搜索框防抖取消
这是最常见的业务场景——用户快速输入时,只保留最后一次请求:
```javascript
function createSearchService() {
let controller = null;
return async function search(query) {
// 取消上一次请求
controller?.abort();
controller = new AbortController();
try {
const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`, {
signal: controller.signal
});
return await res.json();
} catch (err) {
if (err.name === 'AbortError') return null; // 被取消,静默处理
throw err;
}
};
}
```
## 实战:组件卸载时取消请求
以 React 为例,useEffect 返回的清理函数中取消请求:
```javascript
useEffect(() => {
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
.then(res => res.json())
.then(data => setData(data))
.catch(err => {
if (err.name !== 'AbortError') console.error(err);
});
return () => controller.abort();
}, []);
```
## 面试追问与边界问题
**Q:Promise.all 中某一个被取消,其他的会怎样?**
不会怎样。Promise.all 只关心结果,取消是通过外部机制(如 AbortController)实现的。如果想让所有请求共享同一个取消信号,传同一个 signal 即可。
**Q:async/await 中如何取消?**
await 只是语法糖,取消方式完全一样——传 signal 给底层 API,用 try/catch 捕获 AbortError。
**Q:取消后的 Promise 内存怎么回收?**
取消本身不会自动回收。需要确保:清理定时器、移除事件监听、断开网络连接。AbortController 的 signal 用 `{ once: true }` 绑定监听器,触发后自动移除,这是最佳实践。前端5月27日 23:32
Promise 有哪几种状态?状态如何转换?## 状态与转换规则
Promise 有三种核心状态,理解状态转换是掌握 Promise 的基础:
- **pending**:初始状态,异步操作尚未完成
- **fulfilled**:操作成功,触发 `.then()` 回调
- **rejected**:操作失败,触发 `.catch()` 回调
状态转换只能发生一次:pending → fulfilled 或 pending → rejected,一旦改变不可逆。多次调用 `resolve` 或 `reject`,只有第一次生效。
此外还有一个派生状态 **settled**(已定型),表示 Promise 已完成(无论成功或失败),此时会触发 `.finally()` 回调。settled 不是独立状态,而是 fulfilled 和 rejected 的统称。
```javascript
const p = new Promise((resolve, reject) => {
resolve('第一次'); // 生效,状态变为 fulfilled
resolve('第二次'); // 忽略,状态已不可变
reject('失败'); // 忽略
});
p.then(val => console.log(val)); // "第一次"
```
## then 返回值与新 Promise 状态
`.then()` 返回的是一个新的 Promise,它的状态由回调函数的返回值决定:
- 返回普通值 → 新 Promise 变为 fulfilled,值为该返回值
- 抛出错误 → 新 Promise 变为 rejected
- 返回另一个 Promise → 新 Promise 的状态跟随该 Promise
```javascript
Promise.resolve(1)
.then(val => val + 1) // 返回 2,新 Promise fulfilled
.then(val => { // val 为 2
throw new Error('出错了'); // 新 Promise rejected
})
.catch(err => 100) // 捕获错误,返回 100
.then(val => console.log(val)); // 100
```
## 错误处理与穿透机制
Promise 的错误会沿链向下传递,直到遇到 `.catch()`。如果 `.then()` 没有提供第二个参数(错误回调),错误会自动穿透到下一个 `.catch()`:
```javascript
fetch('/api/data')
.then(res => res.json()) // 如果 fetch 失败,错误穿透到 catch
.then(data => processData(data)) // 如果上一步失败,继续穿透
.catch(err => console.error('请求失败:', err));
```
注意:`.then(onFulfilled, onRejected)` 中,`onRejected` 只捕获前一步的错误,不能捕获 `onFulfilled` 自身的错误。推荐统一使用 `.catch()`。
## 静态方法对比
| 方法 | 全部成功 | 有失败 | 返回值 |
|------|---------|--------|--------|
| `Promise.all()` | 返回所有结果 | 第一个失败的原因 | 数组 |
| `Promise.allSettled()` | 返回所有状态和结果 | 不会失败 | `{status, value/reason}[]` |
| `Promise.race()` | 第一个完成的结果 | 第一个失败的原因 | 单个值 |
| `Promise.any()` | 第一个成功的结果 | 全部失败时返回 AggregateError | 单个值 |
`Promise.all()` 适合"全部成功才继续"的场景(如并行请求多个接口)。`Promise.allSettled()` 适合"不管成败都要结果"(如批量操作后统计)。`Promise.any()` 适合"取最快成功的"(如多源竞速)。
## 微任务与执行顺序
Promise 的 `.then()`/`.catch()`/`.finally()` 回调属于微任务,在当前宏任务结束后、下一个宏任务开始前执行。微任务优先级高于宏任务:
```javascript
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// 输出顺序:1 → 4 → 3 → 2
```
面试中常考的变体:在 `then` 回调中嵌套 `setTimeout`,或 `async/await` 与 `Promise.then` 的混合执行顺序。
## 常见陷阱
**1. then 中的 throw 被 catch 捕获,但同步代码中的 throw 不会:**
```javascript
Promise.resolve()
.then(() => { throw new Error('then中抛出'); })
.catch(e => console.log('捕获:', e.message)); // 捕获: then中抛出
```
**2. catch 之后还能继续 then:**
`.catch()` 本身也返回 Promise,后续可以接 `.then()` 继续执行。
**3. Promise 构造函数中的同步错误:**
```javascript
const p = new Promise(() => {
throw new Error('构造函数中抛出'); // 会被 Promise 内部捕获,p 变为 rejected
});
```
**4. 返回值是 thenable 对象(有 then 方法的对象)会被当作 Promise 处理:**
```javascript
Promise.resolve().then(() => {
return { then(resolve) { resolve('thenable'); } };
}).then(val => console.log(val)); // "thenable"
```
## async/await 本质
async/await 是 Promise 的语法糖。async 函数始终返回 Promise,await 暂停执行直到 Promise 完成。错误处理使用 try/catch,比 `.catch()` 更符合同步代码的直觉写法:
```javascript
async function fetchUser() {
try {
const res = await fetch('/api/user');
const user = await res.json();
return user;
} catch (err) {
console.error('获取用户失败:', err);
}
}
```
注意:`await` 只能在 async 函数内使用(顶层 await 需 ES2022 模块环境)。多个独立异步操作不要串行 await,应使用 `Promise.all()` 并行处理。前端5月27日 23:31
Promise 的常见陷阱和最佳实践有哪些?## 常见陷阱
### 忘记返回 Promise
这是 Promise 链中最容易犯的错误。`then` 回调中的返回值会作为下一个 `then` 的输入,忘记 `return` 会导致链断裂:
```javascript
// 错误:then 中忘记 return
function fetchUser() {
getUser().then(user => {
return getPosts(user.id); // 没有 return,外层拿不到结果
});
}
fetchUser().then(posts => console.log(posts)); // undefined
// 正确:return 让 Promise 链延续
function fetchUser() {
return getUser().then(user => {
return getPosts(user.id);
});
}
fetchUser().then(posts => console.log(posts)); // posts 数据
```
### 在 then 中嵌套 Promise
嵌套写法失去了 Promise 链的核心优势——扁平化异步流程:
```javascript
// 错误:回调地狱的 Promise 版
getUser().then(user => {
getPosts(user.id).then(posts => { // 嵌套了
getComments(posts[0].id).then(comments => {
// 越嵌越深
});
});
});
// 正确:链式扁平调用
getUser()
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => console.log(comments));
```
### 忘记 catch
未捕获的 Promise rejection 在 Node.js 中会导致进程退出(`unhandledRejection`),在浏览器中则静默失败,排查困难:
```javascript
// 错误:请求失败无任何提示
fetch('/api/data').then(r => r.json()).then(data => render(data));
// 正确:至少捕获错误
fetch('/api/data')
.then(r => r.json())
.then(data => render(data))
.catch(err => console.error('请求失败:', err));
```
**追问:** `.then(func).catch(handler)` 和 `.then(func, handler)` 有什么区别?
`catch` 能捕获 `func` 内部的异常,而 `.then(null, handler)` 的第二个参数只处理上一个 Promise 的 rejection,捕获不到 `func` 抛出的错误。
### 循环中顺序 await
当多个异步操作互不依赖时,逐个 `await` 会白白浪费时间:
```javascript
// 错误:串行等待,3 个请求耗时 = 3 × 单次耗时
async function loadDashboard() {
const user = await fetchUser(); // 等 1s
const posts = await fetchPosts(); // 再等 1s
const stats = await fetchStats(); // 再等 1s
return { user, posts, stats };
}
// 正确:并行发起,3 个请求耗时 ≈ 单次耗时
async function loadDashboard() {
const [user, posts, stats] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchStats()
]);
return { user, posts, stats };
}
```
### 混用 async/await 和 .then()
两种风格混用会让代码风格不一致,增加阅读负担:
```javascript
// 错误:同一个函数里混用两种写法
async function getData() {
const res = await fetch('/api');
return res.json().then(data => { // 突然切到 .then
return transform(data);
});
}
// 正确:统一用 async/await
async function getData() {
const res = await fetch('/api');
const data = await res.json();
return transform(data);
}
```
### 不必要的 Promise 包装
已经返回 Promise 的函数不需要再用 `new Promise` 包一层:
```javascript
// 错误:反模式 —— Promise 构造函数包装
function getData() {
return new Promise((resolve, reject) => {
fetch('/api') // fetch 本身就返回 Promise
.then(r => r.json())
.then(resolve)
.catch(reject);
});
}
// 正确:直接返回
function getData() {
return fetch('/api').then(r => r.json());
}
```
这种写法被称为 **deferred anti-pattern**,不仅多余,还会吞掉 `resolve`/`reject` 回调中的异常。
### 构造函数中执行异步操作
构造函数必须同步返回实例,无法 `await`,导致实例属性可能处于未就绪状态:
```javascript
// 错误:data 可能为 null
class UserService {
constructor(id) {
this.data = null;
fetch(`/api/users/${id}`)
.then(r => r.json())
.then(data => { this.data = data; });
}
}
const svc = new UserService(1);
console.log(svc.data); // null —— 请求还没完成
// 正确:静态工厂方法
class UserService {
constructor(data) { this.data = data; }
static async create(id) {
const data = await fetch(`/api/users/${id}`).then(r => r.json());
return new UserService(data);
}
}
const svc = await UserService.create(1);
console.log(svc.data); // 有数据
```
### 误用 Promise.all 替代条件请求
`Promise.all` 会等所有请求完成,如果部分请求的结果并不需要,就是浪费:
```javascript
// 错误:无条件并发所有请求
async function getPage(cond) {
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]);
return cond ? a : b; // c 永远用不到,但请求已经发了
}
// 正确:按需请求
async function getPage(cond) {
if (cond) return { a: await fetchA() };
return { b: await fetchB() };
}
```
## 最佳实践
### 用 Promise.allSettled 处理部分失败
`Promise.all` 只要有一个失败就整体失败,而 `allSettled` 会等全部完成,适合"能拿多少拿多少"的场景:
```javascript
async function fetchAll(urls) {
const results = await Promise.allSettled(urls.map(u => fetch(u).then(r => r.json())));
const ok = results.filter(r => r.status === 'fulfilled').map(r => r.value);
const failed = results.filter(r => r.status === 'rejected').map(r => r.reason);
return { ok, failed };
}
```
### 用 Promise.race 实现超时控制
```javascript
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error(`超时 ${ms}ms`)), ms)
);
return Promise.race([promise, timeout]);
}
// 用法
const data = await withTimeout(fetch('/api'), 5000);
```
### 用 Promise.any 获取最快成功结果
多个数据源竞争时,`Promise.any` 返回第一个 fulfilled 的结果,只有全部失败才抛 `AggregateError`:
```javascript
async function getFastest(urls) {
try {
const res = await Promise.any(urls.map(u => fetch(u)));
return await res.json();
} catch (e) {
// e 是 AggregateError,包含所有失败原因
throw new Error('所有数据源均不可用');
}
}
```
### 用 AbortController 取消请求
```javascript
const controller = new AbortController();
async function search(query) {
const res = await fetch(`/api/search?q=${query}`, {
signal: controller.signal
});
return res.json();
}
// 用户输入新关键词时取消上一次请求
input.addEventListener('input', () => {
controller.abort();
search(input.value);
});
```
### 实现带退避的重试机制
```javascript
async function retry(fn, max = 3) {
for (let i = 0; i < max; i++) {
try {
return await fn();
} catch (err) {
if (i === max - 1) throw err;
await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i))); // 指数退避
}
}
}
```
### 用 finally 做资源清理
`finally` 无论成功失败都会执行,适合关闭连接、隐藏 loading 等场景:
```javascript
async function query() {
let conn;
try {
conn = await pool.getConnection();
return await conn.query('SELECT * FROM users');
} finally {
conn?.release(); // 无论是否抛异常都会释放连接
}
}
```
### 请求去重
同一时刻对同一资源发起多次请求是浪费,可以用 Map 缓存正在进行的 Promise:
```javascript
const pending = new Map();
function fetchOnce(url) {
if (pending.has(url)) return pending.get(url);
const p = fetch(url)
.then(r => r.json())
.finally(() => pending.delete(url));
pending.set(url, p);
return p;
}
```
### 并发控制
`Promise.all` 一次全部发出,当数量大时可能打爆服务端。用一个简单的并发池控制:
```javascript
async function concurrent(tasks, limit) {
const results = [];
const executing = new Set();
for (const task of tasks) {
const p = task().then(r => { executing.delete(p); return r; });
executing.add(p);
results.push(p);
if (executing.size >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
// 最多同时 3 个请求
await concurrent(urls.map(u => () => fetch(u).then(r => r.json())), 3);
```
## 易错辨析
**Promise.then() 返回的是同一个 Promise 吗?**
不是。每次 `.then()` 都会返回一个新的 Promise,这也是链式调用的基础:
```javascript
const p1 = fetch('/');
const p2 = p1.then(r => r.json());
const p3 = p2.then(data => data.id);
console.log(p1 === p2); // false
console.log(p2 === p3); // false
```
**await 一个非 Promise 值会怎样?**
会立即 resolve。`await 42` 等价于 `await Promise.resolve(42)`,不会阻塞后续微任务。但在 `for...of` 中加 `await` 会拖慢循环,即使值不是 Promise。
**为什么 catch 之后还能继续 then?**
`catch` 返回的也是新 Promise,且状态为 fulfilled(除非 catch 回调内又抛异常),所以后面可以继续 `.then()`:
```javascript
Promise.reject('err')
.catch(e => 'recovered') // 返回 fulfilled('recovered')
.then(val => console.log(val)); // 'recovered'
```
前端5月27日 23:25
Promise 微任务什么时候执行?事件循环怎么跑的?面试常问这道题,本质是在考察你对 JS 异步执行顺序的理解。核心答案:微任务在当前宏任务结束后、下一个宏任务开始前全部执行完毕;Promise 的 then/catch/finally 回调属于微任务,会在所有同步代码之后、setTimeout 之前执行。
## 事件循环的执行顺序
记住这个流程就够了:
1. 执行同步代码(调用栈)
2. 清空微任务队列(全部执行)
3. 取一个宏任务执行
4. 回到步骤 2,循环往复
所以微任务不是"尽快执行",而是"在当前宏任务结束后立即执行"。这是理解所有输出顺序题的根基。
## 微任务和宏任务有哪些
微任务:Promise.then/catch/finally、queueMicrotask()、MutationObserver、async/await 中 await 后面的代码。
宏任务:setTimeout、setInterval、setImmediate(Node)、I/O、UI 渲染。
## 经典输出顺序题
```javascript
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// 输出:1 → 4 → 3 → 2
```
同步代码先跑(1、4),然后清空微任务(3),最后执行宏任务(2)。
## 链式 then 的执行顺序
```javascript
Promise.resolve()
.then(() => console.log("1"))
.then(() => console.log("2"))
.then(() => console.log("3"));
Promise.resolve()
.then(() => console.log("4"))
.then(() => console.log("5"));
// 输出:1 → 4 → 2 → 5 → 3
```
每个 then 返回新 Promise,下一个 then 注册为该 Promise 的微任务。两根链条交替推进,按注册顺序轮流执行。
## 嵌套 Promise 怎么跑
```javascript
Promise.resolve()
.then(() => {
console.log("1");
Promise.resolve().then(() => console.log("2"));
})
.then(() => console.log("3"));
// 输出:1 → 2 → 3
```
第一个 then 执行时注册了内层微任务,外层第二个 then 也在此时被注册。当前微任务轮次里,两个微任务都已入队,按先进先出执行:先 2 后 3。
## async/await 和微任务的关系
```javascript
async function foo() {
console.log("1");
await bar();
console.log("2"); // 这行是微任务
}
function bar() {
console.log("3");
}
foo();
console.log("4");
// 输出:1 → 3 → 4 → 2
```
await 后面的代码等价于放到 then 回调里,是微任务。这是 async/await 的本质——语法糖。
## Node.js 的差异
Node.js 中 process.nextTick 优先级比 Promise.then 更高,会先于微任务队列执行。另外 Node 11 之后,每个宏任务结束后也会清空微任务,行为已与浏览器一致。
## 追问:微任务会阻塞渲染吗
会。微任务在渲染前执行,如果微任务队列过长,页面就会卡住。所以不要在微任务里做密集计算,该用 setTimeout 让出主线程时就用。前端5月27日 01:17
Promise 和 async/await 和 Callback 有什么区别?三个阶段的异步方案,层层递进:
**Callback**:把后续操作作为回调函数传给异步操作。问题是回调地狱——多层嵌套横向增长,错误处理每个回调都得单独处理。
**Promise**:把回调包装成对象,链式 `.then()` 解决横向嵌套,`.catch()` 统一处理错误。但长链仍不够直观,且 `.then()` 里不能直接用 `try-catch`。
**async/await**:Promise 的语法糖。`async` 函数返回 Promise,`await` 暂停执行等结果。写法就是同步代码的样子,错误用 `try-catch`。本质还是 Promise——`await` 的值就是 `.then()` 回调的参数。
```javascript
// 三个方案的同一操作
// Callback
getData((err, data) => { if (err) return; process(data); });
// Promise
getData().then(process).catch(handleError);
// async/await
try { const data = await getData(); process(data); } catch { handleError(); }
```
## 追问
### async/await 怎么处理并发请求?
`Promise.all([fetch1, fetch2])` 配合 `await`。不要写成 `await fetch1(); await fetch2()`——这样是串行的,第二个请求等第一个完成才发。
### async 函数返回的 Promise 和普通 Promise 有区别吗?
没有本质区别。async 函数内部抛错等于 reject,return 值等于 resolve。唯一注意的是:async 函数返回的 Promise 是原生 Promise,即使你 return 的是一个 thenable 对象,也会自动包裹成 Promise。
前端2024年7月18日 00:34
在Promise中,使用catch和then的第二个参数有什么区别?在Promise中,`.catch()`方法和`.then()`的第二个参数都用于处理Promise中发生的错误或拒绝(rejection)情况,但它们之间存在几个关键的区别:
1. **范围的不同**:
- `.catch()`能够捕获在Promise链中任何之前的错误,包括前面的`.then()`中发生的错误。
- `.then()`的第二个参数仅捕获它直接之前的Promise中的错误。
2. **链式调用的影响**:
- 使用`.catch()`处理错误时,如果`.catch()`里面没有再次抛出错误,Promise链会继续执行后续的`.then()`方法。
- 使用`.then()`的第二个参数处理错误,处理完错误后还会继续执行该`.then()`后续的`.then()`方法,不过这种用法使得代码的错误处理部分和成功处理部分耦合度较高。
3. **代码清晰性**:
- `.catch()`使得错误处理逻辑集中和明确,更易于管理和维护。
- `.then()`的第二个参数虽然功能相似,但可能会使得代码阅读和维护起来较为混乱,因为成功逻辑和错误处理逻辑都包含在同一个方法内。
总的来说,推荐使用`.catch()`来进行错误处理,因为它能提供更清晰、更强大且易于管理的错误捕获机制。前端2024年6月24日 16:43
Promise 是如何实现链式调用的?Promise 实现链式调用主要依赖于其返回一个新的 Promise 对象的特性。
在 JavaScript 中,Promise 是一个处理异步操作的对象,可以在原调用位置以同步方式处理异步操作结果。
下面是 Promise 的链式调用的基本实现:
1. Promise 构造函数接收一个执行函数,执行函数接收两个参数:resolve 和 reject,分别用于异步操作成功与失败的情况。
2. 调用 Promise 对象的 `.then` 方法提供链式调用。`.then` 方法接收两个参数(都是可选的):`onFulfilled` 和 `onRejected`,分别在 Promise 成功或失败时调用。`.then` 方法也返回一个 Promise 对象,以便进行链式调用。
3. 如果 `onFulfilled` 或 `onRejected` 返回一个值 x,运行 Promise 解决过程:[[Promise Resolution Procedure]](https://promisesaplus.com/#the-promise-resolution-procedure)。
4. 如果 `onFulfilled` 或 `onRejected` 抛出一个异常 e,`Promise.then` 的返回的 Promise 对象会被 reject 掉。
5. 如果 `onFulfilled` 不是函数且 promise1(前一个 promise) 成功执行,promise2(下一个 promise)成功处理 promise1 的 final state。
6. 如果 `onRejected` 不是函数且 promise1 失败,promise2 会拒绝 promise1 的原因。
以下是一个示例:
```javascript
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); // 第一步:创建一个 Promise 并执行一个异步操作
}).then(function(result) { // 第二步:注册一个 onFulfilled 回调
console.log(result); // 打印:1
return result + 2;
}).then(function(result) { // 第三步:链式调用
console.log(result); // 打印:3
return result + 2;
}).then(function(result) {
console.log(result); // 打印:5
return result + 2;
});
```
在这个例子中,每个 `.then` 调用后都返回一个新的 Promise 对象,这个新的 Promise 对象会立即执行,并在执行完毕后调用下一个 `.then` 注册的回调。通过这种方式,我们可以以同步的方式处理异步的结果,而这就是 Promise 链式调用的本质。
前端2024年6月24日 16:43
如何基于 Promise.all 实现Ajax请求的串行和并行?### Ajax请求的串行实现
对于串行执行多个Ajax请求,我们通常需要确保一个请求完全完成后,再执行下一个请求。这可以通过链式调用`then`方法来实现,也就是在每个Promise对象的`then`方法中启动下一个Ajax请求。
```javascript
function ajaxRequest(url) {
return new Promise((resolve, reject) => {
// 这里是Ajax请求的代码,成功时调用resolve,失败时调用reject
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
}
const urls = ['/url1', '/url2', '/url3']; // 假设我们有多个请求需要串行处理
let promiseChain = Promise.resolve(); // 初始化一个已完成的Promise
urls.forEach(url => {
promiseChain = promiseChain.then(() => ajaxRequest(url)).then(response => {
console.log('请求完成:', response);
// 这里可以处理每个请求的响应
});
});
// 最后可以在所有请求都完成后执行一些操作
promiseChain.then(() => {
console.log('所有请求都已串行完成。');
});
```
在这个例子中,每个请求仅在前一个请求的`then`方法中被调用,这确保了请求的串行执行。
### Ajax请求的并行实现
要并行执行多个Ajax请求,可以使用`Promise.all`方法。`Promise.all`接收一个Promise对象数组,等待所有的Promise对象都成功完成后,它将返回一个新的Promise,这个新Promise将解析为一个结果数组,数组中的每个结果对应于原Promise数组中的每个请求。
```javascript
function ajaxRequest(url) {
return new Promise((resolve, reject) => {
// 这里是Ajax请求的代码,成功时调用resolve,失败时调用reject
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
}
const urls = ['/url1', '/url2', '/url3']; // 假设我们有多个请求需要并行处理
const promises = urls.map(ajaxRequest); // 创建一个包含所有请求的Promise数组
Promise.all(promises).then(responses => {
console.log('所有请求都已并行完成。');
responses.forEach(response => {
console.log('请求完成:', response);
// 这里可以处理每个请求的响应
});
}).catch(error => {
// 如果任何一个请求失败,这里会捕获到错误
console.error('请求失败:', error);
});
```
在这个例子中,`Promise.all`并行地处理所有的Ajax请求,并在所有请求成功完成后,按照请求的顺序输出响应结果。如果任何一个请求失败,`Promise.all`会立即拒绝,并返回第一个遇到的错误。
这两种方法是处理多个Ajax请求时常用的串行和并行模式。根据实际需求选择合适的方式。在实际面试中,可以根据面试官的要求提供更详细的代码实例或解释。前端2024年6月24日 16:43
如何实现Promise的resolve?在JavaScript中,`Promise` 对象是异步编程的一种解决方案。一个 `Promise` 在创建时处于 `pending`(等待)状态,可以通过其 `resolve` 方法转变为 `fulfilled`(成功)状态,或通过其 `reject` 方法转变为 `rejected`(失败)状态。
要实现 `Promise` 的 `resolve`,通常是在异步操作成功完成时调用。下面是一个简单的例子说明如何使用 `Promise` 的 `resolve` 方法:
```javascript
function asyncOperation() {
// 创建一个新的Promise对象
return new Promise((resolve, reject) => {
// 执行异步操作
setTimeout(() => {
const operationWasSuccessful = true; // 假设这是基于异步操作结果的条件
if (operationWasSuccessful) {
resolve('Operation successful'); // 如果操作成功,调用resolve并传递结果
} else {
reject('Operation failed'); // 如果操作失败,调用reject并传递错误信息
}
}, 1000); // 假设这个异步操作需要1秒钟
});
}
asyncOperation()
.then(result => {
console.log(result); // 打印成功结果
})
.catch(error => {
console.error(error); // 打印错误信息
});
```
在上述代码中,`asyncOperation` 函数返回一个新的 `Promise` 对象。在这个 `Promise` 的构造函数中,有两个参数:`resolve` 和 `reject`。这两个参数也是函数,它们被用来分别处理异步操作的成功和失败情况。在异步操作(这里使用 `setTimeout` 模拟)完成后,根据操作的结果调用 `resolve` 或 `reject`。
如果异步操作成功(在这个例子中,我们假设 `operationWasSuccessful` 为 `true`),则调用 `resolve` 函数并传递结果消息 `'Operation successful'`。这将使得 `Promise` 对象的状态变为 `fulfilled`,并将结果传递给随后的 `.then` 方法的回调函数。
如果异步操作失败,就调用 `reject` 函数并传递错误消息 `'Operation failed'`。这将使得 `Promise` 对象状态变为 `rejected`,并将错误信息传递给随后的 `.catch` 方法的回调函数。