私が持っているfetch-api
POST
要求を:
fetch(url, {
method: 'POST',
body: formData,
credentials: 'include'
})
これのデフォルトのタイムアウトは何ですか?3秒や不定秒などの特定の値に設定するにはどうすればよいですか?
私が持っているfetch-api
POST
要求を:
fetch(url, {
method: 'POST',
body: formData,
credentials: 'include'
})
これのデフォルトのタイムアウトは何ですか?3秒や不定秒などの特定の値に設定するにはどうすればよいですか?
回答:
コメントで指摘されているように、元の回答のコードは、約束が解決/拒否された後もタイマーを実行し続けます。
以下のコードはその問題を修正します。
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)
})
})
}
デフォルトは指定されていません。仕様では、タイムアウトについてはまったく説明されていません。
一般に、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()
すでに解決されているpromiseを呼び出しても何も起こらないため、タイムアウトが続くことは問題ではありません。
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
失敗を処理し()、タイムアウトがまだ発生していない場合は再スローすることで解決できます。
プロミスレースソリューションを使用すると、リクエストがハングしたままになり、バックグラウンドで帯域幅が消費され、処理中の同時リクエストの最大許容数が低下します。
代わりに、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は、フェッチだけでなく、読み取り/書き込み可能なストリームにも使用できます。より新しい関数(特にpromiseベースの関数)はこれをますます使用します。NodeJSは、そのストリーム/ファイルシステムにもAbortControllerを実装しています。私はウェブBluetoothもそれを調べていることを知っています
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));
};
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
}
});
お役に立てば幸いです。
フェッチ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);
}
});
}
編集:フェッチリクエストは引き続きバックグラウンドで実行され、コンソールにエラーが記録される可能性があります。
確かにPromise.race
アプローチはより良いです。
参照については、このリンクを参照してくださいPromise.race()
レースとは、すべてのプロミスが同時に実行され、プロミスの1つが値を返すとすぐにレースが停止することを意味します。したがって、1つの値のみが返されます。フェッチがタイムアウトした場合に呼び出す関数を渡すこともできます。
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;
});
}
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(...)
基になる接続を実際にキャンセルすることはありませんが、promiseをタイムアウトすることができます。
参照
fetchTimeout (url,options,timeout=3000) {
return new Promise( (resolve, reject) => {
fetch(url, options)
.then(resolve,reject)
setTimeout(reject,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