我有一个fetch-api POST请求:
fetch(url, {
method: 'POST',
body: formData,
credentials: 'include'
})
我想知道这个的默认超时时间是多少?我们如何将它设置为特定的值,比如3秒或不定秒?
我有一个fetch-api POST请求:
fetch(url, {
method: 'POST',
body: formData,
credentials: 'include'
})
我想知道这个的默认超时时间是多少?我们如何将它设置为特定的值,比如3秒或不定秒?
当前回答
您可以创建一个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(...)
它实际上不会取消底层连接,但允许您超时承诺。 参考
其他回答
使用承诺竞争解决方案将使请求挂起,仍然在后台消耗带宽,并降低在处理过程中所允许的最大并发请求。
相反,使用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)
})
您也可以使用新添加的AbortSignal.timeout(5000)…但是现在大多数浏览器都没有很好地实现它。现在所有的绿色环境都有这个。您将失去手动关闭请求的控制。上传和下载都必须在5秒内完成
// a polyfill for it would be:
AbortSignal.timeout ??= function timeout(ms) {
const ctrl = new AbortController()
setTimeout(() => ctrl.close(), ms)
return ctrl.signal
}
fetch(url, { signal: AbortSignal.timeout(5000) })
AbortController也可以用于其他事情,不仅是获取,还可以用于可读/可写流。越来越多的新函数(特别是基于承诺的函数)将越来越多地使用它。NodeJS也在它的流/文件系统中实现了AbortController。我知道网络蓝牙也在研究它。现在它也可以与addEventListener选项一起使用,并在信号结束时停止监听
正确的错误处理技巧
正常的做法:
为了在大多数情况下增加超时支持,建议引入一个Promise实用函数,如下所示:
function fetchWithTimeout(resource, { signal, timeout, ...options } = {}) {
const controller = new AbortController();
if (signal != null) signal.addEventListener("abort", controller.abort);
const id = timeout != null ? setTimeout(controller.abort, timeout) : undefined;
return fetch(resource, {
...options,
signal: controller.signal
}).finally(() => {
if (id != null) clearTimeout(id);
});
}
调用控制器。abort或拒绝setTimeout回调函数中的承诺会扭曲堆栈跟踪。
这是次优的,因为如果需要对错误后日志进行分析,就必须在调用fetch方法的函数中添加带有日志消息的样板错误处理程序。
好专业知识:
为了保留错误及其堆栈跟踪,可以应用以下技术:
function sleep(ms = 0, signal) { return new Promise((resolve, reject) => { const id = setTimeout(() => resolve(), ms); signal?.addEventListener("abort", () => { clearTimeout(id); reject(); }); }); } async function fetch( resource, options ) { const { timeout, signal, ...ropts } = options ?? {}; const controller = new AbortController(); let sleepController; try { signal?.addEventListener("abort", () => controller.abort()); const request = nodeFetch(resource, { ...ropts, signal: controller.signal, }); if (timeout != null) { sleepController = new AbortController(); const aborter = sleep(timeout, sleepController.signal); const race = await Promise.race([aborter, request]); if (race == null) controller.abort(); } return request; } finally { sleepController?.abort(); } } (async () => { try { await fetchWithTimeout(new URL(window.location.href), { timeout: 5 }); } catch (error) { console.error("Error in test", error); } })();
在取回API中还没有超时支持。但这可以通过一个承诺来实现。
如。
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);
}
});
}
您可以创建一个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(...)
它实际上不会取消底层连接,但允许您超时承诺。 参考
下面是一个使用NodeJS的SSCCE,它将在1000ms后超时:
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);
});