UNPKG

@canlooks/ajax

Version:
169 lines (168 loc) 5.75 kB
import { AbortError, AjaxError, NetworkError, TimeoutError } from './error'; import { bodyTransform, findBodyFiles } from './util'; export async function core(config) { let { url, params, onUploadProgress, onDownloadProgress, timeout = !onUploadProgress && !onDownloadProgress ? 60_000 : void 0, responseType = onDownloadProgress ? void 0 : 'json', ...init } = config; /** * ------------------------------------------------------------------ * URL */ if (!url) { throw new AjaxError(`"url" is required`, { config }); } if (params) { if (!(params instanceof URLSearchParams)) { params = new URLSearchParams(params); } if (params.size) { if (url instanceof URL) { for (const [name, value] of params) { url.searchParams.set(name, value); } } else { url += `${url.includes('?') ? '&' : '?'}${params}`; } } } /** * ------------------------------------------------------------------ * 超时与中断 */ let abortController; let timeoutId; if (timeout) { abortController ||= new AbortController(); timeoutId = setTimeout(() => { abortController.abort(new TimeoutError(void 0, { config })); }, timeout); } if (config.signal) { abortController ||= new AbortController(); config.signal.addEventListener('abort', () => { abortController.abort(new AbortError(void 0, { config })); }); } /** * ------------------------------------------------------------------ * 请求 */ let response; const { body } = init; try { response = await fetch(url, { ...init, body: bodyTransform(body), signal: abortController?.signal, }); } catch (e) { console.error(e); throw e instanceof AjaxError ? e : new NetworkError(void 0, { config }); } if (!response.ok) { throw new NetworkError(`request failed with status ${response.status}`, { config, response }); } let result; /** * ------------------------------------------------------------------ * 进度 */ try { if (onUploadProgress) { const blob = body instanceof ReadableStream ? await new Response(body).blob() : findBodyFiles(body); if (blob) { const reader = blob.stream().getReader(); let loaded = 0; const total = blob.size; const read = async () => { const { done, value } = await reader.read(); if (done) { return; } onUploadProgress({ loaded: loaded += value.byteLength, total, chunk: value }); await read(); }; await read(); } } if (onDownloadProgress) { const contentLength = response.headers.get('content-length'); if (contentLength && response.body) { let data = new Uint8Array(); const writableStream = new WritableStream({ write(chunk) { const totalLength = data.byteLength + chunk.byteLength; const newData = new Uint8Array(totalLength); newData.set(data); newData.set(chunk, data.byteLength); data = newData; onDownloadProgress({ loaded: data.byteLength, total: +contentLength, chunk }); }, close() { result = data; } }); await response.body.pipeTo(writableStream); } } } catch (e) { console.error(e); throw e instanceof AjaxError ? e : new AjaxError(void 0, { config, response }); } /** * ------------------------------------------------------------------ * 响应 */ clearTimeout(timeoutId); if (onDownloadProgress) { switch (responseType) { case 'arrayBuffer': result = result.buffer; break; case 'blob': const blob = new Blob([result]); result = blob; break; case void 0: break; default: throw new AjaxError(`"${responseType}" is not supported when using "onDownloadProgress"`, { config, response }); } } else { try { switch (responseType) { case 'json': result = await response.json(); break; case 'text': result = await response.text(); break; case 'blob': result = await response.blob(); break; case 'arrayBuffer': result = await response.arrayBuffer(); break; case 'formData': result = await response.formData(); } } catch (e) { console.error(e); throw e instanceof AjaxError ? e : new AjaxError(void 0, { config, response }); } } return { result, response, config }; }