Chore: add timeout on speedtest API fetching

This commit is contained in:
SukkaW 2023-12-24 16:44:26 +08:00
parent 5be5a4e0f6
commit d83f8ccd04
2 changed files with 26 additions and 12 deletions

View File

@ -13,7 +13,7 @@ import { getGorhillPublicSuffixPromise } from './lib/get-gorhill-publicsuffix';
const s = new Sema(3); const s = new Sema(3);
const latestTopUserAgentsPromise = fetchWithRetry('https://unpkg.com/top-user-agents@latest/index.json') const latestTopUserAgentsPromise = fetchWithRetry('https://unpkg.com/top-user-agents@latest/index.json')
.then(res => res.json() as Promise<string[]>); .then(res => res.json<string[]>());
const querySpeedtestApi = async (keyword: string): Promise<Array<string | null>> => { const querySpeedtestApi = async (keyword: string): Promise<Array<string | null>> => {
const topUserAgents = (await Promise.all([ const topUserAgents = (await Promise.all([
@ -39,13 +39,17 @@ const querySpeedtestApi = async (keyword: string): Promise<Array<string | null>>
'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-Site': 'same-origin',
'Sec-Gpc': '1' 'Sec-Gpc': '1'
} },
retry: {
retryOnAborted: true
},
signal: AbortSignal.timeout(4000)
}); });
if (!res.ok) { if (!res.ok) {
throw new Error(`${res.statusText}\n${await res.text()}`); throw new Error(`${res.statusText}\n${await res.text()}`);
} }
const json = await res.json() as Array<{ url: string }>; const json = await res.json<Array<{ url: string }>>();
s.release(); s.release();

View File

@ -42,11 +42,16 @@ interface FetchRetryOpt {
factor?: number, factor?: number,
maxRetryAfter?: number, maxRetryAfter?: number,
retry?: number, retry?: number,
onRetry?: (err: Error) => void onRetry?: (err: Error) => void,
retryOnAborted?: boolean
} }
function createFetchRetry($fetch: typeof fetch): typeof fetch { interface FetchWithRetry {
const fetchRetry = async (url: string | URL, opts: RequestInit & { retry?: FetchRetryOpt } = {}) => { (url: string | URL | Request, opts?: RequestInit & { retry?: FetchRetryOpt }): Promise<Response>
}
function createFetchRetry($fetch: typeof fetch): FetchWithRetry {
const fetchRetry: FetchWithRetry = async (url, opts = {}) => {
const retryOpts = Object.assign( const retryOpts = Object.assign(
{ {
// timeouts will be [10, 60, 360, 2160, 12960] // timeouts will be [10, 60, 360, 2160, 12960]
@ -54,13 +59,14 @@ function createFetchRetry($fetch: typeof fetch): typeof fetch {
minTimeout: MIN_TIMEOUT, minTimeout: MIN_TIMEOUT,
retries: MAX_RETRIES, retries: MAX_RETRIES,
factor: FACTOR, factor: FACTOR,
maxRetryAfter: MAX_RETRY_AFTER maxRetryAfter: MAX_RETRY_AFTER,
retryOnAborted: false
}, },
opts.retry opts.retry
); );
try { try {
return await retry(async (bail) => { return await retry<Response>(async (bail) => {
try { try {
// this will be retried // this will be retried
const res = (await $fetch(url, opts)) as Response; const res = (await $fetch(url, opts)) as Response;
@ -87,13 +93,17 @@ function createFetchRetry($fetch: typeof fetch): typeof fetch {
err.name === 'AbortError' err.name === 'AbortError'
|| ('digest' in err && err.digest === 'AbortError') || ('digest' in err && err.digest === 'AbortError')
) { ) {
console.log(picocolors.gray('[fetch abort]'), picocolors.gray(url.toString())); if (!retryOpts.retryOnAborted) {
return bail(err); console.log(picocolors.gray('[fetch abort]'), url);
return bail(err) as never;
}
} }
} }
if (isClientError(err)) { if (isClientError(err)) {
return bail(err); return bail(err) as never;
} }
console.log(picocolors.gray('[fetch fail]'), url);
throw err; throw err;
} }
}, retryOpts); }, retryOpts);
@ -110,7 +120,7 @@ function createFetchRetry($fetch: typeof fetch): typeof fetch {
fetchRetry[key] = $fetch[key]; fetchRetry[key] = $fetch[key];
} }
return fetchRetry as typeof fetch; return fetchRetry;
} }
export const defaultRequestInit: RequestInit = { export const defaultRequestInit: RequestInit = {