获取重试请求(失败时)

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

我正在使用浏览器的原生 fetch API 进行网络请求。此外,我正在为不受支持的浏览器使用 whatwg-fetch polyfill。

但是,如果请求失败,我需要重试。现在有这个 npm 包 whatwg-fetch-retry 我发现了,但他们没有在他们的文档中解释如何使用它。有人可以帮我解决这个问题或建议我另一种选择吗?

javascript fetch-api polyfills
5个回答
27
投票

来自获取文档:

fetch('/users')
    .then(checkStatus)
    .then(parseJSON)
    .then(function(data) {
          console.log('succeeded', data)
    }).catch(function(error) {
          console.log('request failed', error)
    })

看到那个渔获物了吗? fetch失败时会触发,可以在那里重新fetch。 看看Promise API。

实现示例:

function wait(delay){
    return new Promise((resolve) => setTimeout(resolve, delay));
}

function fetchRetry(url, delay, tries, fetchOptions = {}) {
    function onError(err){
        triesLeft = tries - 1;
        if(!triesLeft){
            throw err;
        }
        return wait(delay).then(() => fetchRetry(url, delay, triesLeft, fetchOptions));
    }
    return fetch(url,fetchOptions).catch(onError);
}

编辑 1:根据 golopot 的建议,p-retry 是一个不错的选择。

编辑 2:简化示例代码。


11
投票

我建议使用一些库来进行承诺重试,例如 p-retry.

例子:

const pRetry = require('p-retry')
const fetch = require('node-fetch')

async function fetchPage () {
  const response = await fetch('https://stackoverflow.com')

  // Abort retrying if the resource doesn't exist
  if (response.status === 404) {
    throw new pRetry.AbortError(response.statusText)
  }

  return response.blob()
}

;(async () => {
  console.log(await pRetry(fetchPage, {retries: 5}))
})()

7
投票

除非真的有必要,否则我不喜欢递归。管理数量激增的依赖项也是一个问题。这是打字稿中的另一种选择。这很容易翻译成 javascript。

interface retryPromiseOptions<T>  {
    retryCatchIf?:(response:T) => boolean, 
    retryIf?:(response:T) => boolean, 
    retries?:number
}

function retryPromise<T>(promise:() => Promise<T>, options:retryPromiseOptions<T>) {
    const { retryIf = (_:T) => false, retryCatchIf= (_:T) => true, retries = 1} = options
    let _promise = promise();

    for (var i = 1; i < retries; i++)
        _promise = _promise.catch((value) => retryCatchIf(value) ? promise() : Promise.reject(value))
           .then((value) => retryIf(value) ? promise() : Promise.reject(value));
    
    return _promise;
}

并以这种方式使用它...

retryPromise(() => fetch(url),{
    retryIf: (response:Response) => true, // you could check before trying again
    retries: 5
}).then( ... my favorite things ... )

我为浏览器上的获取 API 编写了这个。哪个不会在 500 上发出拒绝。而且我没有实施等待。但是,更重要的是,代码展示了如何使用带有承诺的组合来避免递归。

Javascript 版本:

function retryPromise(promise, options) {
    const { retryIf, retryCatchIf, retries } = { retryIf: () => false, retryCatchIf: () => true, retries: 1, ...options};
    let _promise = promise();

    for (var i = 1; i < retries; i++)
        _promise = _promise.catch((value) => retryCatchIf(value) ? promise() : Promise.reject(value))
           .then((value) => retryIf(value) ? promise() : Promise.reject(value));
    
    return _promise;
}

Javascript 用法:

retryPromise(() => fetch(url),{
    retryIf: (response) => true, // you could check before trying again
    retries: 5
}).then( ... my favorite things ... )

编辑:添加了 js 版本,添加了 retryCatchIf,修复了循环启动。


5
投票

可以轻松地将

fetch(...)
包装在一个循环中,并且
catch
潜在的错误(fetch 仅拒绝网络错误等的返回承诺):

const RETRY_COUNT = 5;

async function fetchRetry(...args) {
  let count = RETRY_COUNT;
  while(count > 0) {
    try {
      return await fetch(...args);
    } catch(error) {
      // logging ?
    }

    // logging / waiting?

    count -= 1;
  }

  throw new Error(`Too many retries`);
}  

0
投票

同时使用 Jonas WilmsIsidrock 答案并添加打字稿类型:

const MAX_NB_RETRY = 5;
const RETRY_DELAY_MS = 200;

export default async function fetchRetry(input: RequestInfo | URL, init?: RequestInit | undefined) {
    let retryLeft = MAX_NB_RETRY;
    while (retryLeft > 0){
        try {
            return await fetch(input, init);
        }
        catch (err) { 
            await sleep(RETRY_DELAY_MS)
        }
        finally {
            retryLeft -= 1;
        }
    }
    throw new Error(`Too many retries`);
}

function sleep(delay: number){
    return new Promise((resolve) => setTimeout(resolve, delay));
}
© www.soinside.com 2019 - 2024. All rights reserved.