Promise递归函数未resolve:代理/无代理请求测试函数重启异常
看起来你的downloadFile函数首次运行时测试请求正常,但重启后Promise就一直处于pending状态无法resolve。结合你给出的代码片段,我梳理了几个最可能的原因,以及对应的修复方案:
1. 缺少错误处理,异常导致Promise"卡壳"
你的Promise只定义了resolve参数,完全没处理请求过程中可能出现的网络错误、超时、代理连接失败、接口报错等异常情况。一旦请求出错,代码会直接卡在Promise里,永远不会触发resolve,重启后这个问题也会持续存在。
修复:补充reject并捕获所有异常
不管你用的是axios、fetch还是其他请求库,都要确保捕获所有异常分支,在错误时调用reject(或者至少调用resolve返回错误状态)。这里以axios为例给你修改代码:
const downloadFile = (configuration, proxy = true) => { return new Promise((resolve, reject) => { // 新增reject参数 let receivedBytes = 0; let totalBytes = 0; // 每次调用都创建独立的axios实例,避免代理配置残留 const axiosInstance = axios.create({ proxy: proxy ? { host: '你的代理IP', port: 你的代理端口 } : false }); const requestFile = { method: 'get', headers: { 'Content-Type': 'application/ld+json', 'Authorization': `Bearer ${configuration.token}` }, url: configuration.url, responseType: 'stream' // 下载文件建议用stream类型 }; axiosInstance(requestFile) .then(response => { totalBytes = parseInt(response.headers['content-length'], 10); response.data.on('data', (chunk) => { receivedBytes += chunk.length; // 这里可以添加进度更新逻辑 }); response.data.on('end', () => { // 下载完成,触发resolve resolve({ success: true, receivedBytes, totalBytes }); }); response.data.on('error', (err) => { // 流处理出错,触发reject reject(new Error(`文件流处理失败: ${err.message}`)); }); }) .catch(err => { // 请求本身出错(比如401、代理连接失败、网络中断) reject(new Error(`请求发起失败: ${err.message}`)); }); }); };
2. 代理实例未重置,重启后资源冲突
如果之前你复用了全局的请求实例(比如全局axios),重启函数时旧的代理配置或连接可能残留,导致新请求无法正常建立连接,进而卡住Promise。
修复:每次调用创建独立的请求实例
不要用全局共享的请求实例,而是在downloadFile内部每次调用都创建新的实例,确保代理配置是当前调用的最新状态——就像上面代码里的axios.create那样。
3. 下载完成的判断逻辑异常
如果你的代码没有正确监听下载完成的事件(比如用错了事件名,或者流没有正常结束),也会导致resolve永远不被触发。
检查点:
- 确认监听了正确的结束事件(比如stream的
end事件,或者fetch的response.text()/response.blob()完成回调) - 验证响应头的
content-length是否正确,避免因为进度计算错误误以为下载未完成
4. 重启时未清理旧请求上下文
如果你的函数运行在持久化环境(比如Electron主进程、Node.js后台服务),之前的Promise实例可能还占用着资源,导致新请求被阻塞。
修复:添加请求取消机制
给每个请求绑定取消令牌,重启时主动取消之前的请求,释放资源:
const downloadFile = (configuration, proxy = true) => { const controller = new AbortController(); // 创建取消控制器 return new Promise((resolve, reject) => { const requestFile = { method: 'get', headers: { 'Content-Type': 'application/ld+json', 'Authorization': `Bearer ${configuration.token}` }, url: configuration.url, signal: controller.signal, // 绑定取消信号 proxy: proxy ? { /* 代理配置 */ } : false }; // 发起请求的逻辑... // 将控制器返回给外部,方便重启时取消旧请求 resolve({ controller, /* 其他下载相关结果 */ }); }); }; // 重启时调用: previousRequestController?.abort(); // 取消旧请求 downloadFile(config, proxy); // 发起新请求
总结
优先检查错误处理逻辑和请求实例的独立性,这两个是最常见的导致Promise无法resolve的原因。如果问题还存在,可以在代码中添加日志,打印请求的各个阶段(发起、接收数据、结束、错误),定位到底是哪个环节没执行到resolve。
内容的提问来源于stack exchange,提问作者s-leg3ndz

