How to add a setTimeout to a fetch promise that us

2019-04-10 04:13发布

问题:

After a certain amount of seconds if a fetch promise has not been resolved I want to show a timeout error to the user.

I've seen some good examples of adding a setTimeout to fetch here: https://github.com/github/fetch/issues/175

But how can I handle timing out a fetch promise that also uses redux? E.g.

export function getData() {
  return (dispatch, getState) => {
    fetch('blah.com/data')
    .then(response => response.json())
    .then(json => dispatch(getDataSuccess(json)))
    .catch(
      error => {
        console.log(error)
      }
    )
      dispatch({
        type: DATA_FETCH_REQUEST
      })
  }
}

Thanks for reading!

回答1:

I've been dying to have a reason use Promise.race, it works perfectly for this use case. Promise.race waits for the first resolve or first reject. So if reject fires first then it will never fire the then on Promise.race. More here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race. Sorry, I didn't get a chance to test the code.

export function getData() {
  return (dispatch, getState) => {
    let timeout = new Promise((resolve, reject) => {
      setTimeout(reject, 300, 'request timed out');
    })
    let fetch = new Promise((resolve, reject) => {
      fetch('blah.com/data')
        .then(response => response.json())
        .then(json => resolve(json))
        .catch(reject)
    })
    return Promise
      .race([timeout, fetch])
      .then(json => dispatch(getDataSuccess(json)))
      .catch(err => dispatch(getDataTimeoutOrError(err)))
  }
}


回答2:

Based on the snippet from github you mentioned, you can probably do something like this:

function timeoutPromise(ms, promise) {
  return new Promise((resolve, reject) => {
    const timeoutId = setTimeout(() => {
      reject(new Error('Timeout'));
    }, ms);
    promise.then(
      (res) => {
        clearTimeout(timeoutId);
        resolve(res);
      },
      (err) => {
        clearTimeout(timeoutId);
        reject(err);
      }
    );
  })
}

export function getData() {
  return (dispatch, getState) => {
    dispatch({type: DATA_FETCH_REQUEST});

    timoutPromise(5000, fetch('blah.com/data'))
    .then(response => response.json())
    .then(json => dispatch(getDataSuccess(json)))
    .catch(
      error => {
        // Change this as necessary
        dispatch({type: DATA_FETCH_FAILED});
        console.log(error);
      }
    );
  }
}

If you want to show an information to the user that the request has failed, you might want to handle the action with the type DATA_FETCH_FAILED.