fix(ssr): clear the budget timer once a raced read answers

The mood and dashboard prefetches and the sign-in location lookup raced
their work against a bare setTimeout that was never cleared, leaving
one live timer behind per render or sign-in after the work had already
answered. A shared helper races the work and clears the timer.
This commit is contained in:
Marc-André Bombeck committed 2026-10-08 22:57:47 +02:00
1 parent dd76b434c4
commit b4e3e22d43
5 files changed
+64 -17

No files matched your search

+2 -6
View File
@@ -18,6 +18,7 @@ import {
type MoodAggregates,
} from "@/lib/insights/mood-aggregates";
import type { MoodDailySeries } from "@/lib/analytics/mood-series";
import { settleWithin } from "@/lib/async/settle-within";
import { resolveModuleMap } from "@/lib/modules/gate";
import { queryKeys } from "@/lib/query-keys";
@@ -32,12 +33,7 @@ import InsightsMoodPageClient from "./page-client";
const PREFETCH_BUDGET_MS = 1_500;
function withinBudget<T>(work: Promise<T>): Promise<T | null> {
return Promise.race([
work,
new Promise<null>((resolve) =>
setTimeout(() => resolve(null), PREFETCH_BUDGET_MS),
),
]).catch(() => null);
return settleWithin(work, PREFETCH_BUDGET_MS, null).catch(() => null);
}
/**
+2 -6
View File
@@ -13,6 +13,7 @@ import {
type BatchWindow,
} from "@/lib/dashboard/batch-chart-types";
import { resolveDashboardLayout } from "@/lib/dashboard-layout";
import { settleWithin } from "@/lib/async/settle-within";
import { loadDailyDigest } from "@/lib/daily/load-digest";
import { readSeriesBatch } from "@/lib/measurements/series-batch-read";
import { queryKeys } from "@/lib/query-keys";
@@ -30,11 +31,6 @@ import DashboardPageClient from "./page-client";
*/
const DIGEST_SOFT_BUDGET_MS = 400;
/** Resolve `undefined` after `ms` — the soft-budget loser in the digest race. */
function softTimeout<T>(ms: number): Promise<T | undefined> {
return new Promise((resolve) => setTimeout(() => resolve(undefined), ms));
}
/**
* Thin RSC wrapper around the (client) dashboard page.
*
@@ -144,7 +140,7 @@ export default async function DashboardPage() {
: Promise.resolve(undefined);
const [digest, series] = await Promise.all([
Promise.race([digestWork, softTimeout(DIGEST_SOFT_BUDGET_MS)]),
settleWithin(digestWork, DIGEST_SOFT_BUDGET_MS, undefined),
seriesWork,
]);