Javascript 获取 API 请求超时?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46946380/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:40:49  来源:igfitidea点击:

Fetch API request timeout?

javascriptajaxfetch-api

提问by Akshay Lokur

I have a fetch-apiPOSTrequest:

我有一个fetch-apiPOST要求:

   fetch(url, {
      method: 'POST',
      body: formData,
      credentials: 'include'
    })

I want to know what is the default timeout for this? and how can we set it to a particular value like 3 seconds or indefinite seconds?

我想知道这个的默认超时时间是多少?我们如何将其设置为特定值,例如 3 秒或无限秒?

采纳答案by Bruce Lee

It doesn't have a specified default; the specificationdoesn'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/175Comment by https://github.com/mislav

如描述https://github.com/github/fetch/issues/175由评论https://github.com/mislav

回答by Karl Adler

I really like the clean approach from this gistusing Promise.race

我真的很喜欢这个要点中使用Promise.race的简洁方法

fetchWithTimeout.js

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

回答by Endless

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

使用 abort 语法,您将能够执行以下操作:

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 AbortControllerpage on MDN.

请参阅MDN 上的AbortController页面。

回答by Aadit M Shah

Building on Endless' excellent answer, I created a helpful utility function.

基于 Endless 的优秀答案,我创建了一个有用的实用程序功能。

const fetchTimeout = (url, ms, { signal, ...options } = {}) => {
    const controller = new AbortController();
    const promise = fetch(url, { signal: controller.signal, ...options });
    if (signal) signal.addEventListener("abort", () => controller.abort());
    const timeout = setTimeout(() => controller.abort(), ms);
    return promise.finally(() => clearTimeout(timeout));
};
  1. If the timeout is reached before the resource is fetched then the fetch is aborted.
  2. If the resource is fetched before the timeout is reached then the timeout is cleared.
  3. If the input signal is aborted then the fetch is aborted and the timeout is cleared.
  1. 如果在获取资源之前达到超时,则中止获取。
  2. 如果在达到超时之前获取资源,则清除超时。
  3. 如果中止输入信号,则中止提取并清除超时。
const controller = new AbortController();

document.querySelector("button.cancel").addEventListener("click", () => controller.abort());

fetchTimeout("example.json", 5000, { signal: controller.signal })
    .then(response => response.json())
    .then(console.log)
    .catch(error => {
        if (error.name === "AbortError") {
            // fetch aborted either due to timeout or due to user clicking the cancel button
        } else {
            // network error or json parsing error
        }
    });

Hope that helps.

希望有帮助。

回答by code-jaff

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

fetch API 中还没有超时支持。但是可以通过将其包装在承诺中来实现。

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

回答by Arroganz

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.

事实上,Promise.race 方法更好。

See this link for reference Promise.race()

请参阅此链接以供参考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.

Race 意味着所有 Promise 将同时运行,一旦其中一个 Promise 返回值,比赛就会停止。因此,只会返回一个值。如果提取超时,您还可以传递一个函数来调用。

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

回答by Pulkit Aggarwal

You can create a timeoutPromise wrapper

您可以创建一个 timeoutPromise 包装器

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

它实际上不会取消底层连接,但会允许您超时承诺。
参考

回答by Mojimi

  fetchTimeout (url,options,timeout=3000) {
    return new Promise( (resolve, reject) => {
      fetch(url, options)
      .then(resolve,reject)
      setTimeout(reject,timeout);
    })
  }