denops.vim
denops.vim copied to clipboard
Add a way to interrupt background process running on Deno
Cancellation can be challenging to implement, so we aim to provide a method for denops plugins to handle interruptions.
The proposed API could be:
Promise
version
import type { Denops as DenopsOrigin } from "https://deno.land/x/[email protected]/mod.ts";
import { delay } from "https://deno.land/[email protected]/async/mod.ts";
export function main(denops: Denops): void {
denops.dispatcher = {
start: async () => {
const { interrupted, done } = denops.interrupted();
try {
for (let i = 0; i < 100; i++) {
await Promise.race([interrupted, denops.cmd(`echo 'Hello ${i}'`)]);
await Promise.race([interrupted, delay(100)]);
}
} catch (e) {
if (e instanceof Interrupted) {
await denops.cmd("echo 'Interrupted'");
return;
}
throw e;
} finally {
// Clean up interrupted promise
done();
}
},
};
}
// Assume that this error is thrown from the `signal` when the user invoke `denops#interrupt()`
class Interrupted extends Error {}
// Assume that Denops v6.1 add `interrupted()` method to Denops
type Denops = DenopsOrigin & {
interrupted: () => { interrupted: Promise<never>; done: () => void };
};
The implementation on denops.vim is easier when we use Promise<never>
as a signal but it's not clear for users while users must call done()
or whatever to clean up promises for wait.
AbortSignal
version
import type { Denops as DenopsOrigin } from "https://deno.land/x/[email protected]/mod.ts";
import { abortable, delay } from "https://deno.land/[email protected]/async/mod.ts";
export function main(denops: Denops): void {
denops.dispatcher = {
start: async () => {
const { signal } = denops.interrupted();
try {
for (let i = 0; i < 100; i++) {
signal.throwIfAborted();
await abortable(denops.cmd(`echo 'Hello ${i}'`), signal);
await delay(100, { signal });
}
} catch (e) {
if (e instanceof Interrupted) {
await denops.cmd("echo 'Interrupted'");
return;
}
throw e;
}
},
};
}
// Assume that this error is thrown from the `signal` when the user invoke `denops#interrupt()`
class Interrupted extends Error {}
// Assume that Denops v6.1 add `interrupted()` method to Denops
type Denops = DenopsOrigin & {
interrupted: () => { signal: AbortSignal };
};
The implementation on denops.vim is a bit tough when we use AbortSignal
as a signal but it seems this version is more Deno native friendly and users don't need to care about resource management.