如何在main函数中捕获Promise.all包裹的递归上传函数错误
问题描述
需要实现多文件分片同时上传,每个分片上传失败时最多重试5次。当前递归的uploadFile函数被Promise.all包裹调用,但无法在main函数的catch块中正确捕获错误,请问该如何修复?
示例代码(简化模拟请求逻辑):
class FileUploader { onSuccess: ((this: FileUploader) => void) | null; onFail: ((this: FileUploader) => void) | null; constructor() { this.onSuccess = null; this.onFail = null; } run() { // 模拟请求失败,实际场景是随机成功/失败 this.onFail?.(); } } async function uploadFile(name: string, retryCount = 0) { return new Promise<void>(async (resolve, reject) => { const runner = new FileUploader(); runner.onSuccess = async () => { await new Promise((res) => setTimeout(res, 500)); resolve(); } runner.onFail = async () => { if (retryCount < 5) { console.log(`文件${name}第${retryCount+1}次重试`); const newDelay = Math.pow(2, retryCount) * 100; await new Promise((res) => setTimeout(res, newDelay)); await uploadFile(name, retryCount + 1); } console.log("rejected", name); reject("error"); } runner.run(); }); } async function main() { try { await Promise.all([uploadFile("file1"), uploadFile("file2"), uploadFile("file3")]) console.log("所有文件上传成功") } catch (ex) { console.log("捕获到错误") console.log(ex); } } main();
问题根源
当前代码的核心问题在于:
- 重试时递归调用
uploadFile,但没有将递归后的Promise状态(成功/失败)关联到当前Promise的resolve/reject,导致无论重试是否成功,当前Promise都会执行reject。 - 返回的Promise内部使用了
async,造成不必要的Promise嵌套,增加了状态管理的复杂度。
修复方案
- 关联重试的Promise状态:当需要重试时,调用递归的
uploadFile后,将其结果传递给当前Promise的resolve或reject,而不是直接触发reject。 - 移除Promise内部的async:避免Promise嵌套,简化状态流转逻辑。
- 调整重试后的逻辑:只有当重试次数耗尽时,才执行
reject;否则,将递归调用的结果绑定到当前Promise。
修正后的代码
class FileUploader { onSuccess: ((this: FileUploader) => void) | null; onFail: ((this: FileUploader) => void) | null; constructor() { this.onSuccess = null; this.onFail = null; } run() { // 可以打开下面的注释模拟随机成功/失败 // const num = Math.random(); // if (num > 0.4) { // this.onSuccess?.(); // } else { // this.onFail?.(); // } this.onFail?.(); } } function uploadFile(name: string, retryCount = 0) { return new Promise<void>((resolve, reject) => { const runner = new FileUploader(); runner.onSuccess = () => { setTimeout(() => { console.log(`文件${name}上传成功`); resolve(); }, 500); }; runner.onFail = () => { if (retryCount < 5) { console.log(`文件${name}第${retryCount + 1}次重试`); const newDelay = Math.pow(2, retryCount) * 100; setTimeout(async () => { try { // 将递归上传的结果绑定到当前Promise的resolve/reject await uploadFile(name, retryCount + 1); resolve(); } catch (err) { reject(err); } }, newDelay); } else { console.log(`文件${name}重试5次后仍失败`); reject(new Error(`文件${name}上传失败`)); } }; runner.run(); }); } async function main() { try { await Promise.all([uploadFile("file1"), uploadFile("file2"), uploadFile("file3")]); console.log("所有文件分片上传完成"); } catch (ex) { console.log("全局捕获到上传错误:"); console.log(ex); } } main();
关键修改说明
- 移除了
uploadFile函数的async修饰符,以及内部Promise的async,避免Promise嵌套导致的状态混乱。 - 在重试逻辑中,通过
try/catch捕获递归uploadFile的结果,成功则调用当前Promise的resolve,失败则调用reject,确保当前Promise的状态与重试结果一致。 - 只有当重试次数达到5次上限时,才主动触发
reject,此时错误会被Promise.all捕获,最终传递到main函数的catch块中。
内容的提问来源于stack exchange,提问作者3Dmu
相关产品推荐
相关产品推荐

