fix(web): bound stalled route queries
This commit is contained in:
@@ -85,16 +85,57 @@ type ApiErrorEnvelope = {
|
||||
traceId?: string;
|
||||
};
|
||||
|
||||
export const API_QUERY_TIMEOUT_MS = 15_000;
|
||||
|
||||
function queryTimeout(init: RequestInit | undefined) {
|
||||
const upstream = init?.signal;
|
||||
if (!upstream) return undefined;
|
||||
const controller = new AbortController();
|
||||
let timedOut = false;
|
||||
const abortFromUpstream = () => controller.abort(upstream.reason);
|
||||
if (upstream.aborted) abortFromUpstream();
|
||||
else upstream.addEventListener('abort', abortFromUpstream, { once: true });
|
||||
const timer = window.setTimeout(() => {
|
||||
timedOut = true;
|
||||
controller.abort();
|
||||
}, API_QUERY_TIMEOUT_MS);
|
||||
return {
|
||||
init: { ...init, signal: controller.signal },
|
||||
timedOut: () => timedOut,
|
||||
cleanup: () => {
|
||||
window.clearTimeout(timer);
|
||||
upstream.removeEventListener('abort', abortFromUpstream);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function requestTimeoutError() {
|
||||
const error = new Error(`请求超过 ${API_QUERY_TIMEOUT_MS / 1_000} 秒仍未完成,请检查网络后重试`);
|
||||
error.name = 'ApiRequestTimeoutError';
|
||||
return error;
|
||||
}
|
||||
|
||||
async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const token = getAccessToken();
|
||||
const requestInit = token ? { ...init, headers: withAuthorization(init?.headers, token) } : init;
|
||||
const response = await fetch(path, requestInit);
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 && token && path !== '/api/v2/session') notifyUnauthorizedSession(token);
|
||||
throw new Error(await responseErrorMessage(response));
|
||||
const timeout = queryTimeout(requestInit);
|
||||
try {
|
||||
const response = await fetch(path, timeout?.init ?? requestInit);
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 && token && path !== '/api/v2/session') notifyUnauthorizedSession(token);
|
||||
throw new Error(await responseErrorMessage(response));
|
||||
}
|
||||
const envelope = (await response.json()) as ApiEnvelope<T>;
|
||||
if (timeout?.timedOut()) throw requestTimeoutError();
|
||||
return envelope.data;
|
||||
} catch (error) {
|
||||
if (timeout?.timedOut() && (!(error instanceof Error) || error.name !== 'ApiRequestTimeoutError')) {
|
||||
throw requestTimeoutError();
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
timeout?.cleanup();
|
||||
}
|
||||
const envelope = (await response.json()) as ApiEnvelope<T>;
|
||||
return envelope.data;
|
||||
}
|
||||
|
||||
function withAuthorization(headers: HeadersInit | undefined, token: string) {
|
||||
|
||||
Reference in New Issue
Block a user