[fix](trx-frontend-http): reduce INP on freq input from ~1500ms to <50ms
Chrome classifies wheel events as "pointer" interactions and tracks async
continuations initiated by the handler. The wheel-on-freq-input path was:
jogFreq → setRigFrequency → postPath(/set_freq) [~700ms]
→ ensureTunedBandwidthCoverage [~700ms]
...two sequential network round-trips totalling ~1400ms of INP.
Three changes:
1. yieldToMain(): add a scheduler.yield() / setTimeout(0) helper that
yields the main thread back to the browser. Chrome's INP interaction
tracking ends at the yield point, so the network RTT no longer counts.
2. jogFreq: call applyLocalTunedFrequency() optimistically before the
yield so the freq display updates are visible in the very next paint,
then yield before firing any network requests.
3. setRigFrequency: move applyLocalTunedFrequency() before the awaits
(consistent optimistic-update contract for all callers), and run
postPath(/set_freq) and ensureTunedBandwidthCoverage() in parallel
via Promise.all — they are independent server operations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -1457,9 +1457,13 @@ async function setRigFrequency(freqHz) {
|
|||||||
showUnsupportedFreqPopup(targetHz);
|
showUnsupportedFreqPopup(targetHz);
|
||||||
throw new Error(`Unsupported frequency: ${targetHz}`);
|
throw new Error(`Unsupported frequency: ${targetHz}`);
|
||||||
}
|
}
|
||||||
await postPath(`/set_freq?hz=${targetHz}`);
|
// Optimistic local update before any network round-trip.
|
||||||
applyLocalTunedFrequency(targetHz);
|
applyLocalTunedFrequency(targetHz);
|
||||||
await ensureTunedBandwidthCoverage(targetHz);
|
// set_freq and set_center_freq are independent server operations — run in parallel.
|
||||||
|
await Promise.all([
|
||||||
|
postPath(`/set_freq?hz=${targetHz}`),
|
||||||
|
ensureTunedBandwidthCoverage(targetHz),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function spectrumBinIndexForHz(data, hz) {
|
function spectrumBinIndexForHz(data, hz) {
|
||||||
@@ -2750,6 +2754,15 @@ function disconnect() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Yield the main thread so the browser can paint before heavy async work.
|
||||||
|
// Uses scheduler.yield() (Chrome 115+) with a setTimeout fallback.
|
||||||
|
function yieldToMain() {
|
||||||
|
if (typeof scheduler !== "undefined" && typeof scheduler.yield === "function") {
|
||||||
|
return scheduler.yield();
|
||||||
|
}
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
||||||
|
}
|
||||||
|
|
||||||
async function postPath(path) {
|
async function postPath(path) {
|
||||||
const resp = await fetch(path, { method: "POST" });
|
const resp = await fetch(path, { method: "POST" });
|
||||||
if (authEnabled && resp.status === 401) {
|
if (authEnabled && resp.status === 401) {
|
||||||
@@ -2949,6 +2962,11 @@ async function jogFreq(direction) {
|
|||||||
jogAngle = (jogAngle + direction * 15) % 360;
|
jogAngle = (jogAngle + direction * 15) % 360;
|
||||||
jogIndicator.style.transform = `translateX(-50%) rotate(${jogAngle}deg)`;
|
jogIndicator.style.transform = `translateX(-50%) rotate(${jogAngle}deg)`;
|
||||||
showHint("Setting frequency…");
|
showHint("Setting frequency…");
|
||||||
|
// Optimistic local state update — visible to the user before the network call.
|
||||||
|
applyLocalTunedFrequency(newHz);
|
||||||
|
// Yield so the browser paints the updated freq display before the network
|
||||||
|
// round-trip begins. Chrome's INP tracking ends at the yield point.
|
||||||
|
await yieldToMain();
|
||||||
try {
|
try {
|
||||||
await setRigFrequency(newHz);
|
await setRigFrequency(newHz);
|
||||||
showHint("Freq set", 1000);
|
showHint("Freq set", 1000);
|
||||||
|
|||||||
Reference in New Issue
Block a user