Attempting to switch to a factory of debounced functions.

This commit is contained in:
Andrea Alberti 2024-08-25 13:53:02 +02:00
parent 7a34e8c98f
commit 59da6411e4
2 changed files with 132 additions and 8 deletions

View file

@ -9,6 +9,7 @@ import {
PluginManifest,
FileSystemAdapter,
TAbstractFile,
App,
// PluginSettingTab,
// App,
} from 'obsidian';
@ -20,7 +21,7 @@ import { DEFAULT_SETTINGS } from 'defaults';
import { PluginsAnnotationsSettingTab } from 'settings_tab'
import * as path from 'path';
import { readAnnotationsFromMdFile, writeAnnotationsToMdFile } from 'manageAnnotations';
import { backupSettings, delay, sortAnnotations } from 'utils';
import { backupSettings, debounceFactoryWithWaitMechanism, delay, sortAnnotations } from 'utils';
import { annotationControl } from 'annotation_control';
export default class PluginsAnnotations extends Plugin {
@ -51,6 +52,21 @@ export default class PluginsAnnotations extends Plugin {
private savePromise: Promise<void> | null = null;
private resolveSavePromise: (() => void) | null = null;
// public debouncedSaveAnnotations: (callback: () => void = (): void => {}): void;
constructor(app:App, manifest:PluginManifest) {
super(app, manifest);
// const { waitFnc, debouncedFct } = debounceFactoryWithWaitMechanism(async (...args: unknown[]): Promise<void> => {
// // Extract the callback if it's provided, otherwise default to a no-op function
// const callback = typeof args[0] === 'function' ? args[0] as () => void : () => {};
// await this.saveSettings();
// callback(); // Call the callback after saving settings
// }, 100);
}
async onload() {
// console.clear();
@ -77,6 +93,27 @@ export default class PluginsAnnotations extends Plugin {
}
}
});
const asyncFunc = async (msg: string) => {
console.log("Processing:", msg);
return `Processed: ${msg}`;
};
console.log("HEY");
const { debouncedFct, waitFnc } = debounceFactoryWithWaitMechanism(asyncFunc, 1000);
// Make multiple debounced calls, only the last one should execute
debouncedFct("Message 1");
debouncedFct("Message 2");
// Wait for the last debounced call to finish
waitFnc().then(() => {
console.log("All debounced calls have been completed.");
}).catch(error => {
console.error("An unexpected error occurred:", error);
});
}
/* Load settings for different versions */
@ -229,6 +266,8 @@ export default class PluginsAnnotations extends Plugin {
let isRestoreOperation;
if(data === undefined) {
// we load directly from file, but first wait until
// the previous debounced writing operation is completed
this.waitForSaveToComplete();
data = await this.loadData();
isRestoreOperation = false;
@ -544,6 +583,9 @@ export default class PluginsAnnotations extends Plugin {
descriptionDiv.appendChild(annotation_container);
}
}
const controlDiv = settingItemInfo.querySelector('.setting-item-control');
console.log(controlDiv);
}
}
@ -603,14 +645,14 @@ export default class PluginsAnnotations extends Plugin {
onunload() {
// console.log('Unloading Plugins Annotations');
// Remove all comments
this.removeCommentsFromTab();
// // Remove all comments
// this.removeCommentsFromTab();
// Just in case, disconnect observers if they still exist
this.disconnectObservers();
// // Just in case, disconnect observers if they still exist
// this.disconnectObservers();
// Remove icons
this.removeIcon();
// // Remove icons
// this.removeIcon();
}
getUninstalledPlugins(): PluginAnnotationDict {

View file

@ -112,6 +112,87 @@ export async function createFolderIfNotExists(vault: Vault, folderPath: string)
}
}
// Utility to debounce rebuilds
export function debounceFactory<F extends (...args: unknown[]) => unknown>(func: F, wait: number) {
let timeout: ReturnType<typeof setTimeout>;
return (...args: Parameters<F>): void => {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
export function debounceFactoryWithWaitMechanism<F extends (...args: unknown[]) => ReturnType<F> | Promise<ReturnType<F>>>(func: F, wait: number) {
let timeout: ReturnType<typeof setTimeout> | null = null;
let promise: Promise<ReturnType<F>> | null = null;
let resolvePromise: (() => void) | null = null;
class CancelledExecution extends Error {
constructor(message = 'Debounced function call was cancelled') {
super(message);
this.name = 'CancelledExecution';
}
}
return {
// Function to wait for the completion of the current debounced call (if any)
waitFnc: async (): Promise<void> => {
while (promise) {
try {
await promise;
} catch (error) {
if (!(error instanceof CancelledExecution)) {
throw error; // Re-throw if it's not a cancellation
}
// We do nothing if the execution was cancelled
}
}
},
// The debounced function itself
debouncedFct: (...args: Parameters<F>): Promise<ReturnType<F>> => {
// Clear the previous timeout to cancel any pending execution
if (timeout) {
clearTimeout(timeout);
}
// Store the previous resolvePromise to resolve it after the new promise is created
const previousResolvePromise = resolvePromise;
// Create a new promise for the current execution
promise = new Promise<ReturnType<F>>((resolve, reject) => {
// Now we set the new resolvePromise function
resolvePromise = () => {
reject(new CancelledExecution()); // Reject with a specific cancellation error
};
// After the new promise is created, resolve the previous one
if (previousResolvePromise) {
previousResolvePromise(); // Resolve the previous promise to prevent leaks
}
// Schedule the function to run after the debounce delay
timeout = setTimeout(async () => {
try {
// Await the result of the function and cast it to the expected return type
const result = (await func(...args)) as ReturnType<F>;
resolve(result); // Resolve with the result of the function (sync or async)
} catch (error) {
reject(error); // Reject the promise if the function throws an error
}
// Clear the stored promise and resolve function after execution
promise = null;
resolvePromise = null;
}, wait);
});
// Return the promise, allowing the caller to await the debounced execution
return promise;
}
};
}
/* File suggestions */
export class FileSuggestion extends AbstractInputSuggest<TFile> {
private files:TFile[] = [];
@ -234,4 +315,5 @@ export async function backupSettings(backupName: string, toBeBackedUp: unknown,
export function delay(ms: number) {
return new Promise( resolve => setTimeout(resolve, ms) );
}
}