为何移除async函数catch块中的延迟会导致错误判断失效?
问题描述
我开发了一个每隔几秒向服务器请求数据的处理器,场景里唯一可能出现的错误就是服务器/服务宕机,会抛出ECONNREFUSED或ETIMEDOUT错误,服务器配置确保2分钟内恢复。以下是捕获这两类错误时catch块的代码:
catch (error) { // 处理初始连接问题,比如超时 if (error.code == "ETIMEDOUT" || error.code == "ECONNREFUSED") { console.log(Date() + ": 无法建立与服务器的初始连接,原因是 " + error.code + ".\n服务器正在重试,将在 " + timeoutPeriod/1000 + " 秒后重新尝试.") await new Promise(resolve => setTimeout(resolve, timeoutPeriod)); console.log("重试等待结束."); await getData(); } // 必须加这个延迟,不然上面的错误判断会失效,搞不懂为什么(??????????) await new Promise(resolve => setTimeout(resolve, pollPeriod)); const data = await fetch('url', { method: 'get', headers: headers }); const dataTEXT = await data.text(); if (dataTEXT == "...") return null; }
我搞不懂的是:移除代码最后那个延迟函数后,上面的错误判断if语句会完全失效,加上延迟就一切正常,代码在async函数里。我本来是出于绝望才加的这个延迟,想知道为啥不能移除它?
问题原因及解决思路
核心问题出在错误处理逻辑的流程漏洞上,跟延迟本身没关系,只是延迟碰巧掩盖了这个漏洞:
- 当触发
ETIMEDOUT/ECONNREFUSED错误进入catch块后,你执行了await getData()递归调用,但递归调用完成后,代码会继续执行catch块里剩下的代码——也就是后面的延迟和再次fetch请求。 - 如果没有那个延迟,递归的
getData()如果又触发错误,会再次进入catch块,此时当前catch块的后续代码(fetch)还在排队执行,多个异步调用堆叠会导致事件循环混乱,错误判断的逻辑被打乱,看起来就像if语句失效了。 - 加上延迟后,相当于给递归调用和后续代码之间加了一个“缓冲”,避免了异步调用的堆叠,让错误处理的流程能按预期走。
但这个延迟是治标不治本的,正确的做法是在if语句里处理完错误后终止当前catch块的执行,比如加个return:
catch (error) { if (error.code == "ETIMEDOUT" || error.code == "ECONNREFUSED") { console.log(Date() + ": 无法建立与服务器的初始连接,原因是 " + error.code + ".\n服务器正在重试,将在 " + timeoutPeriod/1000 + " 秒后重新尝试.") await new Promise(resolve => setTimeout(resolve, timeoutPeriod)); console.log("重试等待结束."); await getData(); // 终止当前catch块后续代码执行 return; } const data = await fetch('url', { method: 'get', headers: headers }); const dataTEXT = await data.text(); if (dataTEXT == "...") return null; }
这样不管有没有延迟,错误处理逻辑都能正常工作——因为递归调用后不会再执行当前catch块里的fetch,避免了异步流程冲突。
内容的提问来源于stack exchange,提问作者2002hondaaccord
相关产品推荐
相关产品推荐

