AbortController.abort(reason),但是reason在到达fetch catch子句之前就丢失了

问题描述 投票:0回答:2

我正在实现可中止的获取调用

在我的页面上中止提取基本上有两个原因:

  • 用户决定不再等待 AJAX 数据并单击按钮;在这种情况下,用户界面会显示一条消息“呼叫/任何中断”
  • 用户已经移动到页面的另一部分,不再需要正在获取的数据;在这种情况下,我不希望 UI 显示任何内容,因为它只会让用户感到困惑

为了区分这两种情况,我计划使用

reason
方法的
AbortController.abort
参数,但 fetch 调用中的 .catch 子句总是收到
DOMException('The user aborted a request', ABORT_ERROR)

我尝试提供不同的

DOMException
作为情况 2 中止的原因,但差异丢失了。

有没有人找到如何将有关中止原因的信息发送到 fetch .catch 子句?

javascript google-chrome fetch-api domexception abortcontroller
2个回答
6
投票

在下面的示例中,我演示了如何确定

fetch
请求中止的原因。我提供内嵌注释以进行解释。如果有什么不清楚的地方请随时评论。

重新运行代码片段以查看(可能不同的)随机结果

'use strict';

function delay (ms, value) {
  return new Promise(res => setTimeout(() => res(value), ms));
}

function getRandomInt (min = 0, max = 1) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

// Forward the AbortSignal to fetch:
// https://docs.github.com/en/rest/repos/repos#list-public-repositories
function fetchPublicGHRepos (signal) {
  const headers = new Headers([['accept', 'application/vnd.github+json']]);
  return fetch('https://api.github.com/repositories', {headers, signal});
}

function example () {
  const ac = new AbortController();
  const {signal} = ac;

  const abortWithReason = (reason) => delay(getRandomInt(1, 5))
    .then(() => {
      console.log(`Aborting ${signal.aborted ? 'again ' : ''}(reason: ${reason})`);
      ac.abort(reason);
    });

  // Unless GitHub invests HEAVILY into our internet infrastructure,
  // one of these promises will resolve before the fetch request
  abortWithReason('Reason A');
  abortWithReason('Reason B');

  fetchPublicGHRepos(signal)
    .then(res => console.log(`Fetch succeeded with status: ${res.status}`))
    .catch(ex => {
      // This is how you can determine if the exception was due to abortion
      if (signal.aborted) {
        // This is set by the promise which resolved first
        // and caused the fetch to abort
        const {reason} = signal;
        // Use it to guide your logic...
        console.log(`Fetch aborted with reason: ${reason}`);
      }
      else console.log(`Fetch failed with exception: ${ex}`);
    });

  delay(10).then(() => console.log(`Signal reason: ${signal.reason}`));
}

example();


0
投票

中止原因可通过

reason
属性在信号中获得。如果您自己设置信号,就很容易访问。

https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal

因此,当

fetch
抛出错误时,如果它是带有
DOMException
名称的
'AbortError'
,您可以依赖信号的原因。

const controller = new AbortController();
setTimeout(() => controller.abort('Timeout'), 30000);
try {
  const response = await fetch('some_url', { signal: controller.signal });
  console.log('Fetch response:', response);
} catch (error) {
    if (error instanceof DOMException && error.name === 'AbortError') {
      console.warn('Fetch aborted:', signal.reason);
    } else {
      console.error('Fetch error:', error);
    }
  }
}

您还可以创建自己的获取函数来获取正确的异常:

async function customFetch(url, params) {
  try {
    return fetch(url, params);
  } catch (error) {
    if (error instanceof DOMException && error.name === 'AbortError') {
      // params.signal should be defined at this point,
      // but using the nullish coalesce operators anyway is safer
      params?.signal?.throwIfAborted();
    }
    // Rethrow at catch level in the unlikely case params.signal is undefined
    throw error;
  }
}

通过使用此自定义函数,

fetch
函数在中止时产生的异常将替换为
AbortSignal
异常:

const controller = new AbortController();
setTimeout(() => controller.abort('Timeout'), 30000);
try {
  const response = await customFetch('some_url', { signal: controller.signal });
  console.log('Fetch response:', response);
} catch (error) {
    // Now when the request is aborted, the reason is thrown as a plain string
    if (error === 'Timeout') {
      console.warn('Fetch timeout');
    } else {
      console.error('Fetch error:', error);
    }
  }
}

请注意,在

throwIfAborted
函数中调用信号的
customFetch
可确保抛出的异常是标准的(中止原因为纯字符串),但您也可以抛出自己的异常。

© www.soinside.com 2019 - 2024. All rights reserved.