Fetch API request timeout?

最后都变了- 提交于 2019-11-26 13:05:00
Bruce Lee

It doesn't have a specified default; the specification doesn't discuss timeouts at all.

You can implement your own timeout wrapper for promises in general:

// Rough implementation. Untested.
function timeout(ms, promise) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      reject(new Error("timeout"))
    }, ms)
    promise.then(resolve, reject)
  })
}

timeout(1000, fetch('/hello')).then(function(response) {
  // process response
}).catch(function(error) {
  // might be a timeout error
})

As described in https://github.com/github/fetch/issues/175 Comment by https://github.com/mislav

I really like the clean approach from this gist using Promise.race

fetchWithTimeout.js

export default function (url, options, timeout = 7000) {
    return Promise.race([
        fetch(url, options),
        new Promise((_, reject) =>
            setTimeout(() => reject(new Error('timeout')), timeout)
        )
    ]);
}

main.js

import fetch from './fetchWithTimeout'

// call as usual or with timeout as 3rd argument

fetch('http://google.com', options, 5000) // throw after max 5 seconds timeout error
.then((result) => {
    // handle result
})
.catch((e) => {
    // handle errors and timeout error
})

Using the abort syntax, you'll be able to do:

const controller = new AbortController();
const signal = controller.signal;

const fetchPromise = fetch(url, {signal});

// 5 second timeout:
const timeoutId = setTimeout(() => controller.abort(), 5000);


fetchPromise.then(response => {
  // completed request before timeout fired

  // If you only wanted to timeout the request, not the response, add:
  // clearTimeout(timeoutId);
})

See AbortController page on MDN.

code-jaff

there's no timeout support in the fetch API yet. But it could be achieved by wrapping it in a promise.

for eg.

  function fetchWrapper(url, options, timeout) {
    return new Promise((resolve, reject) => {
      fetch(url, options).then(resolve, reject);

      if (timeout) {
        const e = new Error("Connection timed out");
        setTimeout(reject, timeout, e);
      }
    });
  }

EDIT: The fetch request will still be running in the background and will most likely log an error in your console.

Indeed the Promise.race approach is better.

See this link for reference Promise.race()

Race means that all Promises will run at the same time, and the race will stop as soon as one of the promises returns a value. Therefore, only one value will be returned. You could also pass a function to call if the fetch times out.

fetchWithTimeout(url, {
  method: 'POST',
  body: formData,
  credentials: 'include',
}, 5000, () => { /* do stuff here */ });

If this peeks your interest, a possible implementation would be :

function fetchWithTimeout(url, options, delay, onTimeout) {
   const timer = new Promise((resolve) => {
      setTimeout(resolve, delay, {
      timeout: true,
     });
   });
   return Promise.race([
      fetch(path, request),
      timer
   ]).then(response) {
      if (response.timeout) { 
        onTimeout();
      }
      return response;
   }
}
  fetchTimeout (url,options,timeout=3000) {
    return new Promise( (resolve, reject) => {
      fetch(url, options)
      .then(resolve,reject)
      setTimeout(reject,timeout);
    })
  }
Pulkit Aggarwal

You can create a timeoutPromise wrapper

function timeoutPromise(timeout, err, promise) {
  return new Promise(function(resolve,reject) {
    promise.then(resolve,reject);
    setTimeout(reject.bind(null,err), timeout);
  });
}

You can then wrap any promise

timeoutPromise(100, new Error('Timed Out!'), fetch(...))
  .then(...)
  .catch(...)  

It won't actually cancel an underlying connection but will allow you to timeout a promise.
Reference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!