Pouya92
Pouya92

Reputation: 443

How can I use setTimeout to break a fetch request in react native?

I'm trying to get some data from a server with fetch request. Some times network will fail or similar other things happens and I want to have a timeout to prevent further errors and also have a better experience.

Actually I want to wait for 20 seconds and if I don't get any response I want to show an error and also break the fetch request.

I have a loading modal which I can close it by timeout but I want to break the fetch request either.

here is my fetch request code:

_testPress = async () => { 
        //setTimeout(() => {this.setState({loading: false})}, 20000)
        this.setState({loading : true})
        fetch(getInitUrl('loginUser'), {
        method: 'POST',
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({          
          password : this.state.password,
          email : this.state.emailAddress,            
        }),
      }).then(response => Promise.all([response.ok, response.status ,response.json()]))
      .then(([responseOk,responseStatus, body]) => {        
        if (responseOk) {
          //console.log(responseOk, body);
          this._signInAsync(body.token);
          // handle success case
        } else {
          console.log(responseStatus);
          this.setState({
            showAlert : true,
            alertType : true,
            alertMessage : body.message
          });
        }
      })
      .catch(error => {
        console.error(error);
        // catches error case and if fetch itself rejects
      });
      }

I used setTimeout to close loading module but it wont stop actual request which I want it to stop after 20 seconds.

Please help me with your advice. thx.

Upvotes: 3

Views: 12991

Answers (3)

Hidan Jamanako
Hidan Jamanako

Reputation: 53

You can abort fetch request by passing "signal" into fetch options.

An AbortSignal object instance; allows you to communicate with a fetch request and abort it if desired via an AbortController.

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

Full code example is here.

https://javascript.info/fetch-abort

let controller = new AbortController();
fetch(url, {
  signal: controller.signal
});

controller.abort();

Upvotes: 2

Dave
Dave

Reputation: 2190

There is no standard param you can add to fetch, but you can do this work around:

// creating a wrapper for promises
function timeout(milliseconds, promise) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error("timeout exceeded"))
    }, milliseconds)
    promise.then(resolve, reject)
  })
}

// using that wrapper with fetch
timeout(1000, fetch('/api'))
  .then(function(response) {
     // response success
}).catch(function(error) {
  // timeout error or server error
})

EXAMPLES:

Timeout exceeded:

// creating a wrapper for promises
function timeout(milliseconds, promise) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error("timeout exceeded"))
        }, milliseconds);

        promise.then(resolve, reject);
    });
}
  
const requestErr = new Promise((resolve, reject) => {
    setTimeout(() => {
        // request finished.
        resolve();
    }, 2500);
})

timeout(1000, requestErr)
    .then(function(response) {
        console.log("OK!");
    }).catch(function(error) {
        console.log("ERROR TIMEOUT!");
    });

Timeout not exceeded:

// creating a wrapper for promises
function timeout(milliseconds, promise) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error("timeout exceeded"))
        }, milliseconds);

        promise.then(resolve, reject);
    });
}

const requestOk = new Promise((resolve, reject) => {
    setTimeout(() => {
        // request finished.
        resolve();
    }, 500);
})

timeout(1000, requestOk)
    .then(function(response) {
        console.log("OK!");
    }).catch(function(error) {
        console.log("ERROR TIMEOUT!");
    });

You can also use AXIOS that has its own timeout setting.

Upvotes: 2

brkn
brkn

Reputation: 636

Fetch doesn't implement a connection timeout. You can't abort a fetch request either.

Take a look at this gist, which wraps a Promise.race around the fetch request. The promise will resolve as soon as one of the promises (the fetch or the timeout) resolves or rejects:
https://gist.github.com/davej/728b20518632d97eef1e5a13bf0d05c7

Something like this should work:

Promise.race([
  fetch(getInitUrl('loginUser'), *...fetchparams* ), 
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), 7000)
  )
]).then(*stuff*).catch(*stuff*)

Alternatively, you can also give axios a try, an alternative to fetch, with support for timeouts: https://github.com/axios/axios

Upvotes: 1

Related Questions