Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fetch retry request (on failure)

I'm using browser's native fetch API for network requests. Also I am using the whatwg-fetch polyfill for unsupported browsers.

However I need to retry in case the request fails. Now there is this npm package whatwg-fetch-retry I found, but they haven't explained how to use it in their docs. Can somebody help me with this or suggest me an alternative?

like image 793
dsaket Avatar asked Sep 12 '17 11:09

dsaket


People also ask

Does fetch retry?

Fetch is an asynchronous function, meaning that the program would not wait for result before continuing! n fetches will be called at the same time (kind of), regardless of whether the previous calls succeed! This is not what we want. This is not retry upon failure, this is fetching n times simultaneously!

Does fetch send a GET request?

The fetch() method: Fetch API comes with a fetch () method that allows you to fetch data from all sorts of different places and work with the data fetched. It allows you to make an HTTP request, i.e., either a GET request (for getting data) or POST request (for posting data).

Is fetch a callback?

We start by defining the elf function getGiftForChild() which takes a child's name as input and fetches the gift from our URL using fetch() . This function will return a response in the form of a Promise . In fact, this is also a way of using callbacks.


3 Answers

From the fetch docs :

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

See that catch? Will trigger when fetch fails, you can fetch again there. Have a look at the Promise API.

Implementation example:

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);
}

Edit 1: as suggested by golopot, p-retry is a nice option.

Edit 2: simplified example code.

like image 162
Isidrok Avatar answered Oct 25 '22 05:10

Isidrok


I recommend using some library for promise retry, for example p-retry.

Example:

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}))
})()
like image 9
golopot Avatar answered Oct 25 '22 05:10

golopot


I don't like recursion unless is really necessary. And managing an exploding number of dependencies is also an issue. Here is another alternative in typescript. Which is easy to translate to 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;
}

And use it this way...

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

I wrote this for the fetch API on the browser. Which does not issue a reject on a 500. And did I did not implement a wait. But, more importantly, the code shows how to use composition with promises to avoid recursion.

Javascript version:

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 usage:

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

EDITS: Added js version, added retryCatchIf, fixed the loop start.

like image 5
Arturo Hernandez Avatar answered Oct 25 '22 06:10

Arturo Hernandez