Last active
July 29, 2022 13:23
-
-
Save makadev/d3620995cd99917f9cc96c0d26d7c3aa to your computer and use it in GitHub Desktop.
Simple endless loop (Looper) for NodeJS/TS (or ts-node)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Loop Handler Type | |
*/ | |
type LoopHandler = () => void | never | boolean | Promise<void> | Promise<boolean>; | |
/** | |
* Shutdown Handler Type | |
*/ | |
type ShutdownHandler = () => void; | |
/** | |
* Custom Error Class | |
*/ | |
class LooperError extends Error {} | |
/** | |
* Exception for Fast Exit | |
*/ | |
class LooperExit extends Error {} | |
/** | |
* Looper Singleton | |
*/ | |
class Looper { | |
private static looper?: Looper; | |
public static logHandler = (message: string, extra: string) => { | |
console.log(`${message}: ${extra}`); | |
}; | |
/** | |
* Set true if looper should not reschedule the handler | |
* | |
* @static | |
* @type {boolean} | |
* @memberof Looper | |
*/ | |
public static shouldShutdown: boolean = false; | |
/** | |
* Time in ms to wait between reschedule to reduce CPU Usage | |
* | |
* @static | |
* @type {number} | |
* @memberof Looper | |
*/ | |
public static wait: number = 1000; | |
/** | |
* Loop handler to be executed every cycle | |
*/ | |
public handler: LoopHandler = () => { | |
throw new LooperError("No Loophandler, No Looping, Bye"); | |
}; | |
/** | |
* Handler that will be called on CTRL-C or OS Termination request (SIGINT/SIGTERM) | |
*/ | |
public static shutdownHandler: ShutdownHandler = () => { | |
Looper.shouldShutdown = true; | |
}; | |
/** | |
* Cycle runner | |
* | |
* @returns | |
*/ | |
private async run() { | |
const _instance = Looper.looper; | |
if (_instance === undefined) { | |
throw new LooperError("No Looper, No Looping, Bye"); | |
} | |
// check shutdown condition | |
if (Looper.shouldShutdown) { | |
Looper.logHandler("Graceful Shutdown", "bye"); | |
return; | |
} | |
// execute handler | |
const handler = _instance.handler; | |
try { | |
const res = await handler(); | |
if (typeof res === "boolean" && res === false) { | |
Looper.logHandler("Looper Exit", "termination request"); | |
return; | |
} | |
} catch (e) { | |
if (e instanceof LooperExit) { | |
Looper.logHandler("Looper Exit", e.message); | |
return; | |
} | |
throw e; | |
} | |
// again, check shutdown condition | |
if (Looper.shouldShutdown) { | |
Looper.logHandler("Graceful Shutdown", "bye"); | |
return; | |
} | |
// reschedule for another cycle | |
setTimeout(_instance.run, Looper.wait); | |
} | |
/** | |
* Register handler and startup loop | |
* | |
* @param handler | |
* @param registerShutdownHandler | |
* @returns | |
*/ | |
public static async loop(handler: LoopHandler, registerShutdownHandler: boolean = true) { | |
const instance = new Looper(); | |
instance.handler = handler; | |
Looper.looper = instance; | |
if (registerShutdownHandler) { | |
process.on("SIGTERM", Looper.shutdownHandler); | |
process.on("SIGINT", Looper.shutdownHandler); | |
} | |
await instance.run(); | |
return instance; | |
} | |
} | |
/* | |
* Exec | |
*/ | |
let i = 0; | |
Looper.loop(() => { | |
i++; | |
if (i > 10) throw new LooperExit("no more meeeps!!!"); | |
console.log(`meeeep ${i}`); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment