Skip to main content

Run one-shot and parallel hooks

Run a handler at most once

A hookOnce registration makes its handler eligible to run at most once. Dispatching the same hook again does not invoke that registration again.

import { createHooks } from "hookable";

interface RunOnceHookMap {
"test-hook": () => void;
}

async function runOnceExample() {
const hooks = createHooks<RunOnceHookMap>();
let counter = 0;

hooks.hookOnce("test-hook", () => {
counter++;
});

await hooks.callHook("test-hook");
await hooks.callHook("test-hook");
console.assert(counter === 1);
}

await runOnceExample();

Dispatch asynchronous handlers and remove one

Register two named asynchronous handlers before calling callHookParallel. A synchronous callback throw can prevent later callbacks from starting. A callback-returned rejecting Promise rejects the returned Promise.

import { createHooks } from "hookable";

interface ParallelHookMap {
"app:start": () => void | Promise<void>;
}

async function parallelExecutionExample() {
const hooks = createHooks<ParallelHookMap>();
const trace: string[] = [];
const initAnalytics = async () => {
trace.push("start:analytics");
await new Promise(resolve => setTimeout(resolve, 50));
trace.push("end:analytics");
};
const connectDatabase = async () => {
trace.push("start:db");
await new Promise(resolve => setTimeout(resolve, 100));
trace.push("end:db");
};

hooks.hook("app:start", initAnalytics);
hooks.hook("app:start", connectDatabase);
await hooks.callHookParallel("app:start");
console.log(JSON.stringify(trace));

hooks.removeHook("app:start", initAnalytics);
trace.length = 0;
await hooks.callHookParallel("app:start");
console.log(JSON.stringify(trace));
}

await parallelExecutionExample();

Pass the original function reference to removeHook, clear the trace, and dispatch again to observe the remaining registration.