Async task runner with controlled parallelism (and an angry cat)
npm install machinegun
// Require the class
var Machinegun = require('machinegun');
// Create an instance
var mg = new Machinegun({
barrels: 1, // Number of parallel tasks, 1 => sequential execution
giveUpOnError: false, // Cancel all the tasks if one fails?
ceaseFireOnEmpty: false, // Cease fire when magazine is empty?
fireImmediately: true // Trigger the first task immediately after loading?
});
// A function passed to .load() should either take a callback or return a promise
// It will be triggered by machinegun when it's time to fire this task
mg.load((cb) => {
// The task function should perform some asynchronous operation
var foo = somethingAsynchronous((err) => {
if (err) console.log("I'm errored");
else console.log("I'm succeeded");
// When the operation complete, a callback should be called
cb(err);
// Alternatively the function may return a promise
});
// Hook to machinegun events in order to react to state changes
// while the asynchronous operation is still in progress
mg.on('ceaseFire', foo.pause);
mg.on('fire', foo.resume);
mg.on('giveUp', foo.abort);
});
// If the machinegun was set with fireImmediately == false,
// it will need to be started manually
mg.fire()
// Call .ceaseFire() in order to pause the machinegun
someExternalTrigger.on('pause', () => mg.ceaseFire());
// ... and .fire() to start it again
someExternalTrigger.on('resume', () => mg.fire());
// If you want to abort the execution completely, call .giveUp()
someExternalTrigger.on('abort', () => mg.giveUp());
Module exports a function returning an object. Hence all the below invocation patterns are valid and would produce the same result:
var mg = require('machinegun')(opt);
var Machinegun = require('machinegun');
var mg = new Machinegun(opt);
var machinegun = require('machinegun');
var mg = machinegun(opt);
Machinegun object implements EventEmitter interface. Please refer to the events section below for the full list of emitted events.
Number of parallel task execution conveyors. Defaults to 1
which ensures sequential execution. 0
, or any other falsy value would set unlimited parallelism.
Whether to cancel all running and scheduled tasks in case of an error. Defaults to false
, which means all the tasks will be triggered despite of the errors.
If an error occurs when giveUpOnError
is set to true
, it causes both error
and giveUp
events to be triggered with the error value passed to both as an argument.
Whether to cease fire after the machinegun has emptied. Defaults to false
, which means a task loaded after the machinegun has emptied will fire immediately. If set to true
the machinegun will have to be implicitly restarted by calling .fire()
after emptied.
Whether to trigger first task execution immediately after it has been loaded. Defaults to true
.
If set to false
the machinegun will be initialised in fireCeased
state and will not start untill .fire()
method is called.
Accepts a function, which should either accept a callback parameter or return a promise:
mg.load((cb) => process.nextTick(cb));
mg.load(() => new Promise((resolve, reject) => process.nextTick(resolve)));
If the callback is called with a falsy argument or the promise rejects, an error
event is emitted with an error value or a rejection reason respectively as an argument.
If giveUpOnError
was set to true
the machinegun also gives up, i.e. emits giveUp
event with the same argument and cancels any subsequent tasks.
.load()
will execute on the next tick to allow for event handlers attachment etc.
.load()
returns a machinegun
instance to allow chaining.
Starts tasks execution in case fireImmediately
was set to false
or if the execution was previously paused with .ceaseFire()
. Causes the machinegun to emit fire
event.
.fire()
will execute on the next tick to allow for event handlers attachment etc.
.fire()
returns a machinegun
instance to allow chaining.
Pauses tasks execution until .fire()
is called. Causes the machinegun to emit ceaseFire
event.
.ceaseFire()
executes and fires the event immediately (synchronously).
.ceaseFire()
returns a machinegun
instance to allow chaining.
Aborts execution. No tasks may be added to the machinegun after it has given up. Nor can it be re-started with .fire()
. Causes the machinegun to emit giveUp
event.
Value of the optional reason
argument will be passed to the giveUp
event handler and, subsequently, to a rejection handler of the promise, returned by .promise()
.
.giveUp()
executes and fires the event immediately (synchronously).
.giveUp()
returns a machinegun
instance to allow chaining.
Returns a promise, which resolves to an optional result
argument value when the machinegun empties and rejects with the above reason
when it gives up.
Machinegun will emit error
event if a task has errored. This may be emitted multiple times in case giveUpOnError
was set to false
.
mg.on('error', (err) => console.log("Task errored", err));
Argument passed to the handler will be either a truthy value passed to the callback or a promise rejection reason.
Machinegun will emit giveUp
event if a task has errored and giveUpOnError
was set to true
, or when .giveUp()
method was invoked explicitly.
It is not possible to load more tasks after the machinegun has given up.
mg.on('giveUp', (reason) => console.log("White flags up!", reason));
Machinegun will emit empty
after the last task in the magazine has completed.
This will not be emitted if giveUpOnError
was set to true
and there was an error.
However, if giveUpOnError
was set to false
, it will be emitted even if some tasks have errored.
Please note that it is possible to load in more tasks after the magazine has emptied, hence this event may be emitted multiple times.
mg.on('empty', () => console.log("Magazine empty!"));
If ceaseFireOnEmpty
was set to true
the machinegun will cease fire after emitting this event, also emitting ceaseFire
event. To continue operation .fire()
method has to be called.
Machinegun will emit fire
and ceaseFire
events when respective methods are invoked. ceaseFire
will also be emitted after empty
if ceaseFireOnEmpty
was set to true
.
mg.on('fire', () => console.log("Fire opened"));
mg.on('ceaseFire', () => console.log("Fire ceased"));
Since one task may error while the other one is still running, as well as to support .ceaseFire()
/.fire()
methods, it is advisable to subscribe to some events within the task function:
mg.load((cb) => {
var foo = somethingAsynchronous((err) => cb(err));
mg.on('ceaseFire', foo.pause);
mg.on('fire', foo.resume);
mg.on('giveUp', foo.abort);
});