Skip to content

Instantly share code, notes, and snippets.

@sndrs
Last active August 29, 2015 14:24
Show Gist options
  • Save sndrs/a769577d27944fa8ff63 to your computer and use it in GitHub Desktop.
Save sndrs/a769577d27944fa8ff63 to your computer and use it in GitHub Desktop.
PoC of a JS module loading + monitoring queue – trying to avoid the '9am' rush
// Modules extend a generic queueable module, and add themselves to a global
// queue, which manages running them synchronously, as soon as it can (on the next tick).
// Running order is based on 'high', 'medium' and 'low' priorities, which modules
// can assign themselves.
// The queue also measures how long modules take to run, and if a module exceeds 10ms
// the queue sends a warning (probably to sentry IRL).
// The log() calls work best with chrome devtools, rather than the repl one
// QUEUE TO HANDLE REGISTRATION AND RUNNING OF MODULES
class ModuleQueue {
constructor () {
this.priorities = ['high', 'medium', 'low'];
this.maxRunTime = 10; // no module should take longer than this to run
// this.queue = {high: [],...}
this.queue = this.priorities.reduce((queue, priority) => {
queue[priority] = [];
return queue;
}, {});
// we won't try and run anything if we already are
this.isRunning = false;
}
// add a module to the queue, and start the queue off if it's not already
registerModule (module, {run = true} = {}) {
console.log('registering module', module)
this.queue[module.priority].push(module);
if (run) this.runModules();
}
// look through the queue and run any modules we find, in order
runModules () {
if (!this.isRunning) {
this.isRunning = true;
// make sure we always pull from the top of the queue,
// incase new modules have been registered while running the last
for (let i = 0, len = this.priorities.length ; i < len; i++) {
const module = this.queue[this.priorities[i]].shift();
if (module) {
console.log('found module to run', module)
this.runModule(module);
return;
}
}
// looks like nothing was left
this.isRunning = false;
}
}
// run a given module's init() on the next tick
runModule (module) {
setTimeout(() => {
console.log('running module', module)
const then = Date.now();
module.init(); // init() is the way in for each QueueableModule module
const now = Date.now(), duration = now - then;
console.log('finished running module', module, `in ${duration}ms`);
// handle modules taking too long (probably ping sentry)
if (this.maxRunTime < duration) {
console.log('CAREFULL!!!', module, `TOOK ${duration - this.maxRunTime}ms TOO LONG TO RUN`)
}
this.isRunning = false;
this.runModules();
}, 0);
}
}
// global to house the queue
const moduleQueue = new ModuleQueue();
// QueueableModule = the ur-module.
class QueueableModule {
// if you don't supply an init() method, your class will never be run by the queue
init () {
console.log(`${this.constructor.name}: init() MISSING`);
}
// method that subclasses can call to run themselves
run ({priority = 'medium'} = {}) {
this.priority = priority;
moduleQueue.registerModule(this);
}
}
// ****************** THESE JUST FOR DEMO PURPOSES ******************
// CLASSES THAT EXTEND THE QueueableModule
class MyModule1 extends QueueableModule {};
class MyModule2 extends QueueableModule {
init () {
console.log('hello from an instance of MyModule2');
workHard();
setTimeout(() => {
console.log('hello from an instance of MyModule2 after 2s');
}, 2000)
}
};
class MyModule3 extends QueueableModule {
init () {
console.log('hello from an instance of MyModule3');
workHard();
}
};
// INSTANCES THAT USE THE QUEUE
var myModule1 = new MyModule1();
myModule1.run();
var myModule2 = new MyModule2();
myModule2.run({priority: 'low'});
var myModule3 = new MyModule3();
myModule3.run({priority: 'high'});
// fake a random amount of hard work
function workHard () {
for (var i=0; i<Math.floor(Math.random() * (999999999 - 999999) + 999999);i++){
var y = Math.pow(i,i);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment