Limit city detail fallback to batch failures

This commit is contained in:
2569718930@qq.com
2026-05-31 22:34:28 +08:00
parent d911af1225
commit eecaa48ec2
2 changed files with 34 additions and 21 deletions
@@ -118,6 +118,12 @@ export async function runTests() {
flushCityDetailBatchBlock.includes("payload?.partial === true"),
"partial detail-batch misses should resolve without immediately issuing single-city fallback requests",
);
assert(
flushCityDetailBatchBlock.includes("if (!payload)") &&
flushCityDetailBatchBlock.includes("resolveCityDetailBatchWithSingleFallback") &&
flushCityDetailBatchBlock.includes("resolveBatchWaiters(waiters, null)"),
"single-city detail fallback should be reserved for whole-batch failures rather than successful batch misses",
);
const fetchHourlyBlock = chartLogicSource.match(/async function fetchHourlyForecastForCity[\s\S]*?\r?\n}\r?\n\r?\nfunction fetchCityDetailWithTimeout/)?.[0] || "";
assert(
fetchHourlyBlock.includes("queueCityDetailBatch(city, resParam)") &&
@@ -1118,6 +1118,11 @@ async function flushCityDetailBatch(resolution: string) {
try {
const payload = await fetchCityDetailBatchWithTimeout(cities, resolution);
if (!payload) {
await resolveCityDetailBatchWithSingleFallback(cities, queue, resolution);
return;
}
const details = payload?.details || {};
const partialMissingCities =
payload?.partial === true
@@ -1136,33 +1141,35 @@ async function flushCityDetailBatch(resolution: string) {
resolveBatchWaiters(waiters, null);
return;
}
try {
resolveBatchWaiters(
waiters,
await runQueuedHourlyDetailRequest(() => fetchSingleHourlyForecastForCity(city, resolution)),
);
} catch (error) {
rejectBatchWaiters(waiters, error);
}
resolveBatchWaiters(waiters, null);
}),
);
} catch (error) {
await Promise.all(
cities.map(async (city) => {
const waiters = queue.waiters.get(city);
try {
resolveBatchWaiters(
waiters,
await runQueuedHourlyDetailRequest(() => fetchSingleHourlyForecastForCity(city, resolution)),
);
} catch (fallbackError) {
rejectBatchWaiters(waiters, fallbackError || error);
}
}),
);
await resolveCityDetailBatchWithSingleFallback(cities, queue, resolution, error);
}
}
async function resolveCityDetailBatchWithSingleFallback(
cities: string[],
queue: CityDetailBatchQueue,
resolution: string,
reason?: unknown,
) {
await Promise.all(
cities.map(async (city) => {
const waiters = queue.waiters.get(city);
try {
resolveBatchWaiters(
waiters,
await runQueuedHourlyDetailRequest(() => fetchSingleHourlyForecastForCity(city, resolution)),
);
} catch (fallbackError) {
rejectBatchWaiters(waiters, fallbackError || reason);
}
}),
);
}
function fetchCityDetailBatchWithTimeout(cities: string[], resolution: string) {
const controller = new AbortController();
const timeoutId = globalThis.setTimeout(() => controller.abort(), HOURLY_DETAIL_REQUEST_TIMEOUT_MS);