怎么用 AbortController 取消 fetch 请求?
AbortController 怎么取消 fetch 请求?用 signal 把控制器和请求连起来,调用 abort() 后 fetch 以 AbortError 拒绝。附搜索框取消旧请求、超时取消、一个 signal 取消多个请求的写法和常见坑。
异步与 Promise专题面试题第 1 页,显示第 1–34 题,共找到 34 道完整解析,可继续按分类、标签与关键词缩小范围。
按稳定语义路径排序
AbortController 怎么取消 fetch 请求?用 signal 把控制器和请求连起来,调用 abort() 后 fetch 以 AbortError 拒绝。附搜索框取消旧请求、超时取消、一个 signal 取消多个请求的写法和常见坑。
async 函数里同步 throw 的错误不会直接抛给调用方,而是变成返回的 Promise 的拒绝原因。讲清为什么外层 try/catch 接不住、await 之后才接得住,以及它和普通函数返回 Promise、setTimeout 回调里抛错的区别。
async 函数不管 return 什么,调用方拿到的都是一个 Promise。讲清 return 普通值、return 另一个 Promise、return thenable 分别会怎样,为什么返回的 Promise 和 return 的不是同一个,以及怎么把值取出来。
forEach 会同步调用完所有回调,并丢掉 async 回调返回的 Promise,所以既不按顺序等,外面也不知道什么时候全做完。讲清原因,给出 for...of 串行、Promise.all 加 map 并发两种替代写法,以及错误接不住的坑。
for 循环里每次 await 都要等上一个请求回来才发下一个,是串行,总耗时是所有请求之和。用可运行的计时示例对比串行和 Promise.all 并发,讲清并发的关键是先发起再等待,以及限流、失败处理和什么时候必须串行。
async/await 底层还是 Promise:async 函数返回 Promise,await 后面的代码相当于放进 then 回调里执行。用可运行示例把同一段逻辑写成 await 版和 then 链版,讲清错误处理、finally 的对应关系,以及两种写法不完全等价的地方。
try/catch 只能接住被 await 的 Promise 拒绝和同步抛错,漏写 await、定时器回调里的错误都接不住。讲清哪些业务错误应该就地处理,哪些程序 bug 应该放给全局兜底,以及 return 和 return await 的区别。
限制并发的任务池里,先发的任务不一定先完成,直接 push 结果顺序就乱了。讲清按下标写回结果数组的做法、为什么下标要在 await 之前取,以及按完成顺序处理、按原顺序分段输出这两种变体和失败时的处理。
经典手写题:实现一个 Scheduler,最多同时运行 2 个任务,add 返回 Promise。用一个等待队列加一个运行计数器实现,附 2 3 1 4 输出的完整推演,以及同步抛错、任务失败卡死队列这些常见漏洞。
fetch 重试最常见的坑是参数丢失:Request 对象的请求体只能读一次,第二次发就报 TypeError,options 被拦截器改过也会带错。讲清保存 url 和 options 重发、每次 clone、哪些错误该重试、退避间隔和 POST 幂等问题。
AbortSignal.any 接收一组 signal,返回一个新 signal,其中任意一个被取消它就跟着取消。讲清用户取消加超时的组合写法、怎么通过 reason 区分是谁取消的、已取消的 signal 传进去会怎样,以及老环境下的手写降级。
Promise.all([]) 不会卡住也不会报错,而是返回一个已经 fulfilled、值为空数组的 Promise。讲清它为什么是同步变成 fulfilled、回调却仍然异步执行,以及 race、any、allSettled 遇到空数组时各自的表现和业务里的坑。
Promise.all 只要有一个请求失败就整体 reject,其他成功的结果全拿不到。讲两种保住结果的写法:给每个 Promise 单独 catch 兜底,或者改用 Promise.allSettled,以及关键接口和可选接口混在一起时该怎么拆。
Promise.all 数组里放数字、字符串、null 这类普通值不会报错,它们会被 Promise.resolve 包一层,结果数组里原样保留,位置也不变。讲清包装过程、thenable 对象的特殊处理、结果顺序为什么和完成顺序无关,以及传入不可迭代值时的表现。
Promise.all 是快速失败的:第一个 reject 出现时整体立刻拒绝,不等其他任务。但其他任务不会被停掉,它们照样执行完,结果和后续错误都被静默丢弃。附执行顺序演示、用 AbortController 真正停掉剩余请求的写法和常见误区。
Promise.allSettled 的结果数组里,成功项是 status 加 value,失败项是 status 加 reason,顺序和传入顺序一致。讲清两种结构、为什么要用 status 而不是 value 判断成败、TypeScript 里怎么收窄类型,以及和 Promise.all 的区别。
明明函数已经返回 Promise,还要 new Promise 再包一层,代码多了不说,内层出错时外层 Promise 可能永远 pending。这篇用一段能跑的代码演示错误是怎么丢的,讲清直接 return 原链的改法,以及什么时候才真的需要 new Promise。
Promise.any 只要有一个成功就返回,全部失败时才拒绝,拒绝值是 AggregateError,每个失败原因都在它的 errors 数组里。这篇讲清 errors 的顺序规则、空数组的特殊情况、多节点兜底请求的写法,以及 catch 里怎么区分 AggregateError 和普通错误。
Promise.race 取第一个结束的结果,不管成功还是失败;Promise.any 取第一个成功的,失败会被跳过,全部失败才拒绝。这篇用同一组任务对比两者的输出,讲清超时控制和多节点择优两个典型场景该选哪个,以及空数组时两者的不同表现。
catch 放在链中间,会把错误吃掉并让链恢复成功,后面的 then 照常执行;放在末尾,前面任何一步出错都会跳过剩下的 then 直接进来。这篇用一段对比代码讲清两种位置的输出差异、catch 的返回值怎么决定后续走向,以及 then 第二个参数和 catch 的区别。
then 回调里抛出的异常会被 Promise 自动捕获,变成链上的拒绝,后面的成功回调被跳过,直接进入 catch。这篇用代码演示错误沿链传递和恢复的过程,讲清只有同步抛出才会被捕获、setTimeout 里的 throw 为什么接不住,以及外层 try/catch 为什么无效。
new Promise 传入的 executor 是同步执行的,new 的那一刻就会被立即调用;异步的只是 then 回调。通过一段打印顺序题讲清 executor、resolve 和 then 各自在什么时候运行,以及 executor 里写异步代码、同步抛错时会怎样。
executor 里同步 throw 的错误会被 Promise 构造器捕获,Promise 变成 rejected,错误对象就是拒绝原因。讲清三个边界:resolve 之后再 throw 会被忽略,setTimeout 回调里 throw 构造器接不住,没写 catch 会触发未处理拒绝。
finally 回调拿不到结果,返回值也会被忽略,上一步的成功值或失败原因会原样传下去;只有在 finally 里抛错或返回 rejected Promise,才会改写结果。用可运行的示例讲清这三点,以及用 finally 关 loading 的正确姿势。
then 回调返回一个 Promise 时,下一个 then 拿到的是这个 Promise 最终的值,而不是 Promise 本身。讲清外层 Promise 如何等待并跟随内层的状态、thenable 为什么也会被展开、返回自身为什么报错,以及展开带来的额外微任务。
Promise.race 传入空数组,返回的 Promise 永远停在 pending,既不成功也不失败,await 它的代码会一直卡住。对比 Promise.all、allSettled、any 在空数组下的不同结果,讲清原因和业务代码里该怎么防。
Promise.resolve 收到带 then 方法的对象时,不会原样返回它,而是新建一个 Promise,在下一个微任务里调用它的 then 来决定结果。附原样返回的唯一条件、then 行为不规范时的处理和 await 的关系。
多个 Promise 同时跑,then 回调基本按谁先兑现谁先执行,而不是按创建顺序。但用 Promise 去 resolve 另一个 Promise 会多等两个微任务,顺序就会反过来。附 Promise.all 结果顺序和真实请求里的处理方式。
Promise 只认第一次 resolve 或 reject,之后的调用全部静默忽略,也不会报错。但 resolve 不会让函数停下,后面的代码照样执行。附 resolve 一个 Promise 时的锁定状态、超时封装里的应用和手写 Promise 的实现要点。
哪怕 Promise 早就兑现,then 回调也要等当前同步代码跑完才执行。这篇讲清它被放进了微任务队列、为什么规范要这样设计(避免时同步时异步的回调),以及和 setTimeout、await 的执行顺序关系。
用 Promise.race 让原任务和一个定时拒绝的 Promise 赛跑,就能实现超时。但 race 只是不再等结果,原任务照样执行完。附清理定时器的完整写法、用 AbortController 真正取消请求的方式和常见追问。
Promise 被拒绝后,如果到本轮微任务结束都没有任何处理函数,浏览器就会触发 unhandledrejection。这篇讲清几种最常见的漏处理写法、catch 挂错位置的坑、Node.js 里的默认行为,以及怎样用它做全局错误上报。
异步递归有两个坑:await 之前就递归会同步爆栈,每层都 return 下一层的 Promise 会让内存一直涨。这篇讲清两种问题的来源,为什么尾递归在 JavaScript 里靠不住,以及用 while 循环加队列改写的标准做法。
用 reduce 从 Promise.resolve(初始值) 开始,每一步 prev.then(fn),就能让函数依次执行并拿到上一步的结果。附 async/await 写法、把 fn() 写成立即调用导致变并发的经典错误、错误处理和收集全部结果的方法。