【发布时间】:2023-03-16 04:52:01
【问题描述】:
我有一个fetch-api POST 请求:
fetch(url, {
method: 'POST',
body: formData,
credentials: 'include'
})
我想知道这个的默认超时时间是多少?我们如何将其设置为特定值,例如 3 秒或无限秒?
【问题讨论】:
标签: javascript ajax fetch-api
我有一个fetch-api POST 请求:
fetch(url, {
method: 'POST',
body: formData,
credentials: 'include'
})
我想知道这个的默认超时时间是多少?我们如何将其设置为特定值,例如 3 秒或无限秒?
【问题讨论】:
标签: javascript ajax fetch-api
使用 Promise Race 解决方案会使请求挂起,但仍会在后台消耗带宽,并降低仍在处理中的最大允许并发请求。
而是使用AbortController 来实际中止请求,这是一个示例
const controller = new AbortController()
// 5 second timeout:
const timeoutId = setTimeout(() => controller.abort(), 5000)
fetch(url, { signal: controller.signal }).then(response => {
// completed request before timeout fired
// If you only wanted to timeout the request, not the response, add:
// clearTimeout(timeoutId)
})
AbortController 也可以用于其他事情,不仅可以用于获取,还可以用于可读/可写流。更多更新的功能(特别是基于承诺的功能)将越来越多地使用它。 NodeJS 也在其流/文件系统中实现了 AbortController。我知道网络蓝牙也在研究它。现在它也可以与 addEventListener 选项一起使用,并在信号结束时停止监听
【讨论】:
编辑如果您喜欢更简洁的解决方案来处理所有边缘情况,请选择以下答案:https://stackoverflow.com/a/57888548/1059828。
我真的很喜欢 gist 使用 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
})
【讨论】:
fetch 错误发生在 超时之后,则会导致“未处理的拒绝”。这可以通过处理 (.catch) fetch 失败并在超时尚未发生时重新抛出来解决。
正如 cmets 中所指出的,即使在 promise 被解决/拒绝后,原始答案中的代码也会继续运行计时器。
下面的代码解决了这个问题。
function timeout(ms, promise) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error('TIMEOUT'))
}, ms)
promise
.then(value => {
clearTimeout(timer)
resolve(value)
})
.catch(reason => {
clearTimeout(timer)
reject(reason)
})
})
}
它没有指定的默认值; the specification 根本不讨论超时。
您可以为一般的 Promise 实现自己的超时包装器:
// 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
})
如https://github.com/github/fetch/issues/175 中所述 评论https://github.com/mislav
【讨论】:
.reject() 没有任何作用。
在 Endless 的优秀 answer 的基础上,我创建了一个有用的实用函数。
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));
};
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
}
});
希望对您有所帮助。
【讨论】:
在 fetch API 中还没有超时支持。但可以通过将其包装在一个 Promise 中来实现。
例如。
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);
}
});
}
【讨论】:
编辑:获取请求仍将在后台运行,并且很可能会在您的控制台中记录错误。
确实Promise.race 方法更好。
参考此链接Promise.race()
Race 意味着所有 Promises 将同时运行,并且一旦其中一个 Promise 返回值,比赛就会停止。 因此,只会返回一个值。 如果提取超时,您还可以传递一个函数来调用。
fetchWithTimeout(url, {
method: 'POST',
body: formData,
credentials: 'include',
}, 5000, () => { /* do stuff here */ });
如果这激起了您的兴趣,一个可能的实现是:
function fetchWithTimeout(url, options, delay, onTimeout) {
const timer = new Promise((resolve) => {
setTimeout(resolve, delay, {
timeout: true,
});
});
return Promise.race([
fetch(url, options),
timer
]).then(response => {
if (response.timeout) {
onTimeout();
}
return response;
});
}
【讨论】:
如果您的代码中没有配置超时,它将是您浏览器的默认请求超时。
1) Firefox - 90 秒
在 Firefox URL 字段中输入 about:config。找到键network.http.connection-timeout对应的值
2) Chrome - 300 秒
【讨论】:
fetchTimeout (url,options,timeout=3000) {
return new Promise( (resolve, reject) => {
fetch(url, options)
.then(resolve,reject)
setTimeout(reject,timeout);
})
}
【讨论】:
你可以创建一个 timeoutPromise 包装器
function timeoutPromise(timeout, err, promise) {
return new Promise(function(resolve,reject) {
promise.then(resolve,reject);
setTimeout(reject.bind(null,err), timeout);
});
}
然后你可以包装任何承诺
timeoutPromise(100, new Error('Timed Out!'), fetch(...))
.then(...)
.catch(...)
它实际上不会取消底层连接,但可以让你超时。
Reference
【讨论】:
这是一个使用 NodeJS 的 SSCCE,它将在 1000 毫秒后超时:
import fetch from 'node-fetch';
const controller = new AbortController();
const timeout = setTimeout(() => {
controller.abort();
}, 1000); // will time out after 1000ms
fetch('https://www.yourexample.com', {
signal: controller.signal,
method: 'POST',
body: formData,
credentials: 'include'
}
)
.then(response => response.json())
.then(json => console.log(json))
.catch(err => {
if(err.name === 'AbortError') {
console.log('Timed out');
}}
)
.finally( () => {
clearTimeout(timeout);
});
【讨论】:
使用c-promise2 lib 可取消的超时提取可能看起来像这样(Live jsfiddle demo):
import CPromise from "c-promise2"; // npm package
function fetchWithTimeout(url, {timeout, ...fetchOptions}= {}) {
return new CPromise((resolve, reject, {signal}) => {
fetch(url, {...fetchOptions, signal}).then(resolve, reject)
}, timeout)
}
const chain = fetchWithTimeout("https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=10s", {timeout: 5000})
.then(request=> console.log('done'));
// chain.cancel(); - to abort the request before the timeout
此代码为 npm 包cp-fetch
【讨论】: