From 74cd7f1500d730021a0f577c0ecfd7a2460fdb15 Mon Sep 17 00:00:00 2001 From: Ali Mihandoost Date: Fri, 4 Nov 2022 12:04:22 +0330 Subject: [PATCH] feat(fetch): simple memory caching for remove duplicate/parallel requests --- packages/core/fetch/src/fetch.ts | 46 ++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/packages/core/fetch/src/fetch.ts b/packages/core/fetch/src/fetch.ts index 6217a8b44..fc1fae6ea 100644 --- a/packages/core/fetch/src/fetch.ts +++ b/packages/core/fetch/src/fetch.ts @@ -7,13 +7,6 @@ alwatrRegisteredList.push({ version: '{{ALWATR_VERSION}}', }); -declare global { - // Patch typescript's global types - interface AbortController { - abort(reason?: string): void; - } -} - export type CacheStrategy = 'network_only' | 'network_first' | 'cache_only' | 'cache_first' | 'stale_while_revalidate'; export type CacheDuplicate = 'never' | 'always' | 'until_load' | 'auto'; @@ -79,6 +72,11 @@ export interface FetchOptions extends RequestInit { queryParameters?: Record; } +let cacheStorage: Cache; +const cacheSupported = 'caches' in self; + +const duplicateRequestStorage: Record> = {}; + /** * It fetches a JSON file from a URL, and returns the parsed data. * @@ -101,7 +99,7 @@ export async function getJson & {url: string}): Promise { const options = _processOptions(_options); logger.logMethodArgs('fetch', {options}); - return _handleCacheStrategy(options); + return _handleRemoveDuplicate(options); } /** @@ -198,21 +196,32 @@ function _processOptions(options: Partial & {url: string}): FetchO return options as FetchOptions; } -let cacheStorage: Cache; -const cacheSupported = 'caches' in self; +/** + * Handle Remove Duplicates over `_handleCacheStrategy`. + */ +function _handleRemoveDuplicate(options: FetchOptions): Promise { + if (options.removeDuplicate === 'never') return _handleCacheStrategy(options); + + logger.logMethod('_handleRemoveDuplicate'); -// const duplicateRequestStorage: Record> = {}; + duplicateRequestStorage[options.url] ??= _handleCacheStrategy(options); + + if (options.removeDuplicate === 'until_load') { + duplicateRequestStorage[options.url].then(() => delete duplicateRequestStorage[options.url]); + } + + return duplicateRequestStorage[options.url]; +} /** * Handle Cache Strategy over `_handleRetryPattern`. */ -export async function _handleCacheStrategy(options: FetchOptions): Promise { - logger.logMethodArgs('_handleCacheStorage', {options}); - +async function _handleCacheStrategy(options: FetchOptions): Promise { if (options.cacheStrategy === 'network_only') { return _handleRetryPattern(options); } // else handle cache strategies! + logger.logMethod('_handleCacheStrategy'); if (cacheStorage == null) { cacheStorage = await caches.open(options.cacheStorageName); @@ -273,6 +282,8 @@ export async function _handleCacheStrategy(options: FetchOptions): Promise { + if (!(options.retry >= 1)) return _handleTimeout(options); + logger.logMethod('_handleRetryPattern'); const externalAbortSignal = options.signal; @@ -310,7 +321,7 @@ async function _handleRetryPattern(options: FetchOptions): Promise { /** * It's a wrapper around the browser's `fetch` with timeout. */ -async function _handleTimeout(options: FetchOptions): Promise { +function _handleTimeout(options: FetchOptions): Promise { logger.logMethod('_handleTimeout'); return new Promise((resolved, reject) => { // @TODO: AbortController polyfill @@ -337,7 +348,8 @@ async function _handleTimeout(options: FetchOptions): Promise { }); }); - window.fetch(options.url, options) + window + .fetch(options.url, options) .then((response) => resolved(response)) .catch((reason) => reject(reason)) .finally(() => clearTimeout(timeoutId));