Compare commits

...

1 Commits

Author SHA1 Message Date
Gregory Schier
471a099b9b Refactor model sync scheduling 2026-05-08 12:57:22 -07:00
3 changed files with 43 additions and 39 deletions

View File

@@ -1,4 +1,4 @@
import { debounce } from "@yaakapp-internal/lib";
import { debounce, eagerDebounceAsync } from "@yaakapp-internal/lib";
import type { AnyModel, ModelPayload } from "@yaakapp-internal/models";
import { watchWorkspaceFiles } from "@yaakapp-internal/sync";
import { syncWorkspace } from "../commands/commands";
@@ -25,41 +25,8 @@ export async function sync({ force }: { force?: boolean } = {}) {
});
}
const debouncedSync = debounce(async () => {
await sync();
}, 1000);
let modelSyncTimer: ReturnType<typeof setTimeout> | null = null;
let modelSyncInFlight = false;
function scheduleModelSync() {
if (modelSyncTimer == null) {
// No timer means this is the first model change in a burst, so sync immediately.
void syncModelChanges();
} else {
// Keep pushing the trailing sync out until model writes have been quiet for a bit.
clearTimeout(modelSyncTimer);
}
modelSyncTimer = setTimeout(async () => {
modelSyncTimer = null;
// Catch any final state that was written while the immediate sync was running.
await syncModelChanges();
}, 1000);
}
async function syncModelChanges() {
if (modelSyncInFlight) return;
modelSyncInFlight = true;
try {
await sync();
} catch (e) {
console.error(e);
} finally {
modelSyncInFlight = false;
}
}
const syncAfterFileChange = debounce(sync, 1000);
const syncAfterModelWrite = eagerDebounceAsync(sync, 1000);
/**
* Subscribe to model change events. Since we check the workspace ID on sync, we can
@@ -67,7 +34,7 @@ async function syncModelChanges() {
*/
function initModelListeners() {
listenToTauriEvent<ModelPayload>("model_write", (p) => {
if (isModelRelevant(p.payload.model)) scheduleModelSync();
if (isModelRelevant(p.payload.model)) syncAfterModelWrite();
});
}
@@ -82,11 +49,11 @@ function initFileChangeListeners() {
await unsub?.(); // Unsub to previous
const workspaceMeta = jotaiStore.get(activeWorkspaceMetaAtom);
if (workspaceMeta == null || workspaceMeta.settingSyncDir == null) return;
debouncedSync(); // Perform an initial sync when switching workspace
syncAfterFileChange(); // Perform an initial sync when switching workspace
unsub = watchWorkspaceFiles(
workspaceMeta.workspaceId,
workspaceMeta.settingSyncDir,
debouncedSync,
syncAfterFileChange,
);
});
}

View File

@@ -0,0 +1,36 @@
export function eagerDebounceAsync(fn: () => Promise<void>, delay: number) {
let timer: ReturnType<typeof setTimeout> | null = null;
let inFlight: Promise<void> | null = null;
let runAfterInFlight = false;
const run = async () => {
if (inFlight != null) {
runAfterInFlight = true;
return;
}
runAfterInFlight = false;
inFlight = fn()
.catch(console.error)
.finally(() => {
inFlight = null;
if (runAfterInFlight && timer == null) {
void run();
}
});
await inFlight;
};
return () => {
if (timer == null) {
void run();
} else {
clearTimeout(timer);
}
timer = setTimeout(() => {
timer = null;
void run();
}, delay);
};
}

View File

@@ -1,3 +1,4 @@
export * from "./debounce";
export * from "./eagerDebounceAsync";
export * from "./formatSize";
export * from "./templateFunction";