From be9df5af614b51cb58b4ef5c74da9ce4b15c471e Mon Sep 17 00:00:00 2001 From: eric-zaharia Date: Mon, 16 Feb 2026 17:18:25 +0200 Subject: [PATCH 1/5] fix(js): fixed caching performance issues --- .../src/createBrowserLocalStorageCache.ts | 80 +++++++++++-------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts b/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts index 1157bfc75..d362ef3cb 100644 --- a/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts +++ b/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts @@ -2,6 +2,17 @@ import { Cache, CacheEvents } from '@algolia/cache-common'; import { BrowserLocalStorageCacheItem, BrowserLocalStorageOptions } from '.'; +function yieldToMain(): Promise { + // eslint-disable-next-line no-undef + const g: any = typeof globalThis !== 'undefined' ? globalThis : undefined; + + if (g?.scheduler?.yield) { + return g.scheduler.yield(); + } + + return new Promise(resolve => setTimeout(resolve, 0)); +} + export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptions): Cache { const namespaceKey = `algoliasearch-client-js-${options.key}`; @@ -23,30 +34,23 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio getStorage().setItem(namespaceKey, JSON.stringify(namespace)); }; - const removeOutdatedCacheItems = () => { + const getFilteredNamespace = (): Record => { const timeToLive = options.timeToLive ? options.timeToLive * 1000 : null; const namespace = getNamespace(); - const filteredNamespaceWithoutOldFormattedCacheItems = Object.fromEntries( + return Object.fromEntries( Object.entries(namespace).filter(([, cacheItem]) => { - return cacheItem.timestamp !== undefined; - }) - ); - - setNamespace(filteredNamespaceWithoutOldFormattedCacheItems); - - if (!timeToLive) return; + if (cacheItem.timestamp === undefined) { + return false; + } - const filteredNamespaceWithoutExpiredItems = Object.fromEntries( - Object.entries(filteredNamespaceWithoutOldFormattedCacheItems).filter(([, cacheItem]) => { - const currentTimestamp = new Date().getTime(); - const isExpired = cacheItem.timestamp + timeToLive < currentTimestamp; + if (!timeToLive) { + return true; + } - return !isExpired; + return cacheItem.timestamp + timeToLive >= new Date().getTime(); }) ); - - setNamespace(filteredNamespaceWithoutExpiredItems); }; return { @@ -57,25 +61,33 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio miss: () => Promise.resolve(), } ): Readonly> { - return Promise.resolve() - .then(() => { - removeOutdatedCacheItems(); - - const keyAsString = JSON.stringify(key); - - return getNamespace>()[keyAsString]; - }) - .then(value => { - return Promise.all([value ? value.value : defaultValue(), value !== undefined]); - }) - .then(([value, exists]) => { - return Promise.all([value, exists || events.miss(value)]); - }) - .then(([value]) => value); + return Promise.resolve().then(async () => { + await yieldToMain(); + + const namespace = getFilteredNamespace(); + const keyAsString = JSON.stringify(key); + const cachedItem = namespace[keyAsString]; + + setNamespace(namespace); + + // eslint-disable-next-line functional/no-let + let value: TValue; + + if (cachedItem) { + value = cachedItem.value; + } else { + value = await defaultValue(); + await events.miss(value); + } + + return value; + }); }, set(key: object | string, value: TValue): Readonly> { - return Promise.resolve().then(() => { + return Promise.resolve().then(async () => { + await yieldToMain(); + const namespace = getNamespace(); // eslint-disable-next-line functional/immutable-data @@ -91,7 +103,9 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio }, delete(key: object | string): Readonly> { - return Promise.resolve().then(() => { + return Promise.resolve().then(async () => { + await yieldToMain(); + const namespace = getNamespace(); // eslint-disable-next-line functional/immutable-data From 50e39dae36f8610434577a9a9fb467d8a99a167f Mon Sep 17 00:00:00 2001 From: eric-zaharia Date: Mon, 16 Feb 2026 17:52:04 +0200 Subject: [PATCH 2/5] fix(js): fixed build issue --- .../src/createBrowserLocalStorageCache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts b/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts index d362ef3cb..d4a562464 100644 --- a/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts +++ b/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts @@ -6,7 +6,7 @@ function yieldToMain(): Promise { // eslint-disable-next-line no-undef const g: any = typeof globalThis !== 'undefined' ? globalThis : undefined; - if (g?.scheduler?.yield) { + if (g && g.scheduler && g.scheduler.yield) { return g.scheduler.yield(); } From 02b1b4a42ad6896ee00aaaf8f029ea968c7ec53d Mon Sep 17 00:00:00 2001 From: eric-zaharia Date: Mon, 16 Feb 2026 18:18:22 +0200 Subject: [PATCH 3/5] fix(js): removed async/await usage --- .../src/createBrowserLocalStorageCache.ts | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts b/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts index d4a562464..83804301b 100644 --- a/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts +++ b/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts @@ -61,33 +61,27 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio miss: () => Promise.resolve(), } ): Readonly> { - return Promise.resolve().then(async () => { - await yieldToMain(); - + return yieldToMain().then(() => { const namespace = getFilteredNamespace(); const keyAsString = JSON.stringify(key); const cachedItem = namespace[keyAsString]; setNamespace(namespace); - // eslint-disable-next-line functional/no-let - let value: TValue; - if (cachedItem) { - value = cachedItem.value; - } else { - value = await defaultValue(); - await events.miss(value); + return cachedItem.value as TValue; } - return value; + // eslint-disable-next-line promise/no-nesting + return defaultValue().then((value: TValue) => { + // eslint-disable-next-line promise/no-nesting + return events.miss(value).then(() => value); + }); }); }, set(key: object | string, value: TValue): Readonly> { - return Promise.resolve().then(async () => { - await yieldToMain(); - + return yieldToMain().then(() => { const namespace = getNamespace(); // eslint-disable-next-line functional/immutable-data @@ -103,9 +97,7 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio }, delete(key: object | string): Readonly> { - return Promise.resolve().then(async () => { - await yieldToMain(); - + return yieldToMain().then(() => { const namespace = getNamespace(); // eslint-disable-next-line functional/immutable-data From 7432326aedba83922c89030d892eb4558d6575b1 Mon Sep 17 00:00:00 2001 From: eric-zaharia Date: Thu, 19 Feb 2026 15:45:40 +0200 Subject: [PATCH 4/5] fix(js): small refactor --- .../src/createBrowserLocalStorageCache.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts b/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts index 83804301b..914d341f3 100644 --- a/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts +++ b/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts @@ -7,7 +7,9 @@ function yieldToMain(): Promise { const g: any = typeof globalThis !== 'undefined' ? globalThis : undefined; if (g && g.scheduler && g.scheduler.yield) { - return g.scheduler.yield(); + return g.scheduler.yield().catch(() => { + return new Promise(resolve => setTimeout(resolve, 0)); + }); } return new Promise(resolve => setTimeout(resolve, 0)); @@ -37,10 +39,11 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio const getFilteredNamespace = (): Record => { const timeToLive = options.timeToLive ? options.timeToLive * 1000 : null; const namespace = getNamespace(); + const currentTime = new Date().getTime(); return Object.fromEntries( Object.entries(namespace).filter(([, cacheItem]) => { - if (cacheItem.timestamp === undefined) { + if (!cacheItem || cacheItem.timestamp === undefined) { return false; } @@ -48,7 +51,7 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio return true; } - return cacheItem.timestamp + timeToLive >= new Date().getTime(); + return cacheItem.timestamp + timeToLive >= currentTime; }) ); }; @@ -75,7 +78,10 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio // eslint-disable-next-line promise/no-nesting return defaultValue().then((value: TValue) => { // eslint-disable-next-line promise/no-nesting - return events.miss(value).then(() => value); + return events + .miss(value) + .catch(error => console.error(error)) // eslint-disable-line no-console + .then(() => value); }); }); }, From c7b04fb65f3b90ff094f5738777d35fb866d7519 Mon Sep 17 00:00:00 2001 From: eric-zaharia Date: Thu, 19 Feb 2026 17:11:21 +0200 Subject: [PATCH 5/5] fix(js): modified bundle size limit --- package.json | 4 ++-- .../src/createBrowserLocalStorageCache.ts | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index a59a09cd3..2612e716c 100644 --- a/package.json +++ b/package.json @@ -103,11 +103,11 @@ }, { "path": "packages/algoliasearch/dist/algoliasearch-lite.umd.js", - "maxSize": "4.6KB" + "maxSize": "4.7KB" }, { "path": "packages/recommend/dist/recommend.umd.js", - "maxSize": "4.4KB" + "maxSize": "4.5KB" } ] } diff --git a/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts b/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts index 914d341f3..8fc292726 100644 --- a/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts +++ b/packages/cache-browser-local-storage/src/createBrowserLocalStorageCache.ts @@ -7,7 +7,10 @@ function yieldToMain(): Promise { const g: any = typeof globalThis !== 'undefined' ? globalThis : undefined; if (g && g.scheduler && g.scheduler.yield) { - return g.scheduler.yield().catch(() => { + return g.scheduler.yield().catch((error: any) => { + // eslint-disable-next-line no-console + console.error('Failed to yield to main: ', error); + return new Promise(resolve => setTimeout(resolve, 0)); }); } @@ -76,13 +79,7 @@ export function createBrowserLocalStorageCache(options: BrowserLocalStorageOptio } // eslint-disable-next-line promise/no-nesting - return defaultValue().then((value: TValue) => { - // eslint-disable-next-line promise/no-nesting - return events - .miss(value) - .catch(error => console.error(error)) // eslint-disable-line no-console - .then(() => value); - }); + return defaultValue().then((value: TValue) => events.miss(value).then(() => value)); }); },