From c8e3f5b64164f9ba196587e889aec5e0fa82f778 Mon Sep 17 00:00:00 2001 From: Honey Date: Mon, 8 Jun 2026 20:42:03 +0200 Subject: [PATCH] perf: show artist hero after 2 fast calls instead of waiting for all 6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the allSettled into two phases: await imgP+albumP first (both are fast — getArtistImage hits S.artistImgCache when navigating from the listing, getArtist is a local Navidrome call), then reveal the hero. The slow calls (getArtistInfo2 / Last.fm bio, iTunes search, events) keep running in parallel and are only awaited when rendering the content sections below the hero — so the user sees the hero card immediately. Co-Authored-By: Claude Sonnet 4.6 --- src/views/detail.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/views/detail.js b/src/views/detail.js index 052ec3d..809fe05 100644 --- a/src/views/detail.js +++ b/src/views/detail.js @@ -86,16 +86,21 @@ export async function loadArtistDetail(artist) { const hasND = !!artist.id; const knownItunes = artist.itunesArtistId || null; - const [imgRes, albumRes, infoRes, topRes, iArtistRes, eventsRes] = await Promise.allSettled([ - hasND ? getArtistImage(artist.id) : Promise.resolve(null), - hasND ? api('getArtist', `&id=${artist.id}`) : Promise.resolve(null), - hasND ? api('getArtistInfo2',`&id=${artist.id}&count=8`) : Promise.resolve(null), - hasND ? api('getTopSongs', `&artist=${encodeURIComponent(artist.name)}&count=10`) : Promise.resolve(null), - knownItunes - ? Promise.resolve([{ artistId: knownItunes }]) - : itunesSearch(artist.name, 'musicArtist', 5), - fetchEvents(artist.name), - ]); + // Fire all requests immediately so they run in parallel. + // Hero only needs imgP + albumP (both fast / usually cached) — await those + // first so the hero appears instantly without waiting for iTunes or events. + const imgP = hasND ? getArtistImage(artist.id) : Promise.resolve(null); + const albumP = hasND ? api('getArtist', `&id=${artist.id}`) : Promise.resolve(null); + const infoP = hasND ? api('getArtistInfo2',`&id=${artist.id}&count=8`) : Promise.resolve(null); + const topP = hasND ? api('getTopSongs', `&artist=${encodeURIComponent(artist.name)}&count=10`) : Promise.resolve(null); + const iArtistP = knownItunes ? Promise.resolve([{ artistId: knownItunes }]) : itunesSearch(artist.name, 'musicArtist', 5); + const eventsP = fetchEvents(artist.name); + + // Phase 1 — hero (fast: image usually cached from listing, getArtist is local) + const [imgRes, albumRes] = await Promise.allSettled([imgP, albumP]); + + // Phase 2 — content (may be slow: Last.fm bio, iTunes, events) + const [infoRes, topRes, iArtistRes, eventsRes] = await Promise.allSettled([infoP, topP, iArtistP, eventsP]); const _normWords = s => new Set((s || '').toLowerCase().replace(/[^a-z0-9]/g, ' ').split(/\s+/).filter(Boolean)); const _queryWords = _normWords(artist.name);