为何我的Promise会比预期提前resolve?Torn API调用问题排查
问题排查与修复方案
核心问题根源
1. getItemUpdated 函数逻辑错误
你当前的getItemUpdated是async函数返回手动创建的Promise,但executor函数里既没有调用resolve也没有调用reject,导致这个Promise会直接默认resolve为undefined,完全不符合预期。而且async的executor函数本身会导致Promise的异常处理混乱。
2. callTornApi 未捕获网络异常
当axios请求抛出网络错误(比如超时、连接失败)时,代码没有try/catch捕获,会直接终止while循环,导致callTornApi的Promise直接reject,无法完成最多5次重试的逻辑。
3. 入口循环的重试逻辑不可靠
用i--修改for循环计数器的方式,结合await异步逻辑,很容易出现循环次数异常(比如重复处理同一个ID或者跳过ID),而且依赖getItemUpdated的reject才能触发,但当前getItemUpdated根本不会reject。
分步修复代码
1. 修复 getItemUpdated 函数
去掉多余的手动Promise创建,直接利用async函数的特性,正确处理成功和失败的情况:
const getItemUpdated = async (itemId) => { const result = await api.market.getLowestListing(itemId); // 补充你删除的校验逻辑,判断result是否有效 if (!result) { throw new Error(`获取商品${itemId}数据失败`); } // 其他业务处理逻辑放在这里 return result; };
2. 修复 callTornApi 的异常捕获与重试逻辑
给axios请求加上try/catch,确保网络错误也能触发重试,同时用局部变量隔离重试次数,避免并发请求互相干扰:
async callTornApi(url: string, params?): Promise<object | null> { let retries = 0; const maxRetries = 5; while (retries < maxRetries) { try { if (this.debug) { console.log('Calling Torn API... ', url, params); } const { data } = await axios.get(url, { params }); if (data.error) { if (this.debug) { console.log('Wrapper API Error: ', data.error.error, ' Code: ', data.error.code, ' Retries: ', retries); } this.error = { code: data.error.code, message: data.error.error }; if (data.error.code === 2) { this.removeKey(params.key); } await new Promise(resolve => setTimeout(resolve, this.retryTimeout)); retries++; } else { this.error = undefined; return data; } } catch (networkError) { // 捕获网络错误(超时、断网等) if (this.debug) { console.log('Network Error: ', networkError.message, ' Retries: ', retries); } await new Promise(resolve => setTimeout(resolve, this.retryTimeout)); retries++; } } // 重试5次都失败 this.error = { code: -1, message: '达到最大重试次数' }; return null; }
3. 修复入口循环的重试逻辑
用while循环代替for循环,避免修改计数器导致的异常:
let currentIndex = 0; const totalItems = itemIds.length; while (currentIndex < totalItems) { const itemId = itemIds[currentIndex]; try { const res = await getItemUpdated(itemId); if (res) { await informUser(client, itemId); currentIndex++; // 成功后才推进索引 } } catch (err) { console.log(`商品${itemId}请求失败,等待重试...`); await wait(5000); // 失败不推进索引,继续重试当前ID } }
4. 完善 getLowestListing 的错误处理
确保当getItems返回null时,上层能感知失败:
async getLowestListing(itemId: number): Promise<LowestListing | null> { const bazaarItems = await this.bazaar.getItems(itemId, 1); if (this.api.error || !bazaarItems) { // 可选择抛出错误或返回null,根据上层逻辑调整 // throw new Error(`获取最低 listing 失败: ${this.api.error?.message}`); return null; } // 补充你删除的业务处理逻辑,比如提取最低价格数据 return processedResult; }
关键说明
- 永远不要在
Promise构造函数的executor里用async函数,这会导致异常无法被捕获,Promise状态混乱。 - 网络请求的重试必须捕获所有异常,包括axios抛出的网络错误,否则会中断重试流程。
- 循环处理异步任务时,避免修改for循环的计数器,改用while循环控制索引更安全。
内容的提问来源于stack exchange,提问作者OliverKhl
相关产品推荐
相关产品推荐

