如何判断捕获到的fetch错误是否可重试?

前端开发 2026-07-08

我想弄清楚如何判断捕获到的 fetch 错误是否可重试。

try
{
    let response = fetch(url);
}
catch(error)
{
    ...
}

我知道 fetch 在服务器响应错误时不会失败(例如404)

不过,从我看到的情况来看,错误可能被抛出/捕获的一些原因有以下几种:

  • DNS/网络问题 - 可重试
  • 无效的URL方案(如 xhttps://www.google.com)- 不可重试
  • error.name === 'AbortError' - 不可重试

Gemini说这个:

const isRetriable = error.name !== 'AbortError' && /fetch|network|failed/i.test(error.message);

但它对无效的URL方案不起作用,因为它抛出这个错误,其消息中包含 fetch,但并不可重试。

TypeError: Failed to fetch. URL scheme "xhttps" is not supported.
    at ...

如何在 catch 块内,检查 error 是否可重试?

解决方案

你想要像下面这样的吗?如果第一次尝试失败,就进行下一次,若不可重试则直接终止。

另外,为防止无限循环,增加了最大尝试次数

let url = "xhttp://example.com";

call(url);

async function call(url, attempt = 1, maxAttempts = 3) {
  try {
    const response = await fetch(url);

    if (!response.ok) {
      const retryableStatuses = [502, 503, 504];
      if (retryableStatuses.includes(response.status)) {
         console.log(`Server error ${response.status}. Retryable.`);
         // force retry by throwing a custom error to the catch block
         throw new Error(`HTTP_${response.status}`);
      }

      // 404 or 400, non-retryable. Stop here.
      console.error(`Fatal HTTP error: ${response.status}`);
      return;
    }

    // Success case
    const data = await response.json().catch(() => null);
    console.log("Success!", data);

  } catch (error) {
    // enforce max attempts to prevent infinite loops
    if (attempt >= maxAttempts) {
      console.error("Max retry attempts reached. Aborting.");
      return;
    }

    if (error.message.startsWith('HTTP_') || isFetchErrorRetryable(error, url)) {
      console.log(`Retrying attempt ${attempt + 1}...`);

      // just as example, 1 retry with success
      const nextUrl = 'https://httpbin.org/status/200';

      await call(nextUrl, attempt + 1, maxAttempts);
    } else {
      console.error("Fatal error. Do not retry:", error.message);
    }
  }
}

function isFetchErrorRetryable(error, urlString) {
  if (error.name === 'AbortError' || error.name === 'TimeoutError') {
    return false;
  }

  try {
    const parsedUrl = new URL(urlString);
    if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
      return false; 
    }
  } catch (urlError) {
    return false;
  }

  if (error instanceof TypeError && !/failed to fetch|network/i.test(error.message)) {
    return false;
  }

  return true;
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章