From bb751c32ef5b28e1840839c16cfd34394b331236 Mon Sep 17 00:00:00 2001 From: Ralksta <16941050+ralksta@users.noreply.github.com> Date: Tue, 21 Apr 2026 22:51:15 +0200 Subject: [PATCH] perf: add promise deduplication for getAssetInfo (#134) Add Promise deduplication (request coalescing) to the `getAssetInfo` method in `ImmichClient` (`lib/immich.ts`) to prevent concurrent identical requests from firing multiple times to the Immich server. This is particularly helpful when calling `Promise.all` on multiple assets concurrently (e.g. hero images on the homepage or multiple album cover placeholder hashes). Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- lib/immich.ts | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/immich.ts b/lib/immich.ts index 0766919..4484d4b 100644 --- a/lib/immich.ts +++ b/lib/immich.ts @@ -74,6 +74,7 @@ class ImmichClient { private hasLoggedAlbums = false; private pendingAlbumsPromise: Promise | null = null; private pendingAlbumPromises = new Map>(); + private pendingAssetPromises = new Map>(); private get config() { return getConfig(); @@ -344,11 +345,27 @@ class ImmichClient { const cached = cache.get(cacheKey); if (cached) return cached; - const asset = await this.request(`/assets/${encodeURIComponent(assetId)}`); - if (!asset) return null; + // ⚡ Bolt: Deduplicate concurrent requests for the same asset ID. + // If a request for this asset is already in flight (e.g. from Promise.all in a grid), + // return the pending promise instead of triggering a redundant API call. + if (this.pendingAssetPromises.has(assetId)) { + return this.pendingAssetPromises.get(assetId)!; + } - cache.set(cacheKey, asset, this.config.cacheTtl); - return asset; + const promise = (async () => { + try { + const asset = await this.request(`/assets/${encodeURIComponent(assetId)}`); + if (!asset) return null; + + cache.set(cacheKey, asset, this.config.cacheTtl); + return asset; + } finally { + this.pendingAssetPromises.delete(assetId); + } + })(); + + this.pendingAssetPromises.set(assetId, promise); + return promise; } /**