206 lines
6.0 KiB
JavaScript
206 lines
6.0 KiB
JavaScript
/**
|
|
* An instruction is a self contained piece of information that can be created and then later be executed. {@link Run}
|
|
* instance uses this as the values of the `Run.next` queue.
|
|
*
|
|
* @module Run~Instructions
|
|
*/
|
|
var _ = require('lodash'),
|
|
Timings = require('./timings'),
|
|
|
|
arrayProtoSlice = Array.prototype.slice,
|
|
arrayProtoUnshift = Array.prototype.unshift,
|
|
|
|
pool; // function
|
|
|
|
/**
|
|
* Create a new instruction pool
|
|
*
|
|
* @param {Object.<Function>} processors - hash of all command processor functions
|
|
* @returns {InstructionPool}
|
|
*/
|
|
pool = function (processors) {
|
|
!_.isObject(processors) && (processors = {});
|
|
|
|
/**
|
|
* Create a new instruction to be executed later
|
|
*
|
|
* @constructor
|
|
*
|
|
* @param {String} name - name of the instruction. this is useful for later lookup of the `processor` function when
|
|
* deserialising this object
|
|
* @param {Object} [payload] - a **JSON compatible** object that will be forwarded as the 2nd last parameter to the
|
|
* processor.
|
|
* @param {Array} [args] - all the arguments that needs to be passed to the processor is in this array
|
|
* @private
|
|
* @example
|
|
* var inst = Instruction.create(function (arg1, payload, next) {
|
|
* console.log(payload);
|
|
* next(null, 'hello-on-execute with ' + arg1);
|
|
* }, 'sample-instruction', {
|
|
* payloadData1: 'value'
|
|
* }, ['one-arg']);
|
|
*
|
|
* // now, when we do execute, the result will be a console.log of payload and message will be as expected
|
|
* instance.execute(function (err, message) {
|
|
* console.log(message);
|
|
* });
|
|
*
|
|
*/
|
|
var Instruction = function (name, payload, args) {
|
|
var processor = processors[name];
|
|
|
|
if (!_.isString(name) || !_.isFunction(processor)) {
|
|
throw new Error('run-instruction: invalid construction');
|
|
}
|
|
|
|
// ensure that payload is an object so that data storage can be done. also ensure arguments is an array
|
|
!_.isObject(payload) && (payload = {});
|
|
!_.isArray(args) && (args = []);
|
|
|
|
_.assign(this, /** @lends Instruction.prototype */ {
|
|
/**
|
|
* @type {String}
|
|
*/
|
|
action: name,
|
|
|
|
/**
|
|
* @type {Object}
|
|
*/
|
|
payload: payload,
|
|
|
|
/**
|
|
* @type {Array}
|
|
*/
|
|
in: args,
|
|
|
|
/**
|
|
* @type {Timings}
|
|
*/
|
|
timings: Timings.create(),
|
|
|
|
/**
|
|
* @private
|
|
* @type {Function}
|
|
*/
|
|
_processor: processor
|
|
});
|
|
|
|
// record the timing when this instruction was created
|
|
this.timings.record('created');
|
|
};
|
|
|
|
/**
|
|
* Shortcut to `new Instruction(...);`
|
|
*
|
|
* @param {Function} processor
|
|
* @param {String} name
|
|
* @param {Object} [payload]
|
|
* @param {Array} [args]
|
|
*
|
|
* @returns {Instruction}
|
|
*/
|
|
Instruction.create = function (processor, name, payload, args) {
|
|
return new Instruction(processor, name, payload, args);
|
|
};
|
|
|
|
/**
|
|
* Store all thenable items
|
|
*
|
|
* @type {Array}
|
|
*/
|
|
Instruction._queue = [];
|
|
|
|
/**
|
|
* Executes an instruction with previously saved payload and arguments
|
|
*
|
|
* @param {Function} callback
|
|
* @param {*} [scope]
|
|
*
|
|
* @todo: use timeback and control it via options sent during pool creation as an option
|
|
*/
|
|
Instruction.prototype.execute = function (callback, scope) {
|
|
!scope && (scope = this);
|
|
|
|
var params = _.clone(this.in),
|
|
sealed = false,
|
|
|
|
doneAndSpread = function (err) {
|
|
if (sealed) {
|
|
console.error('__postmanruntime_fatal_debug: instruction.execute callback called twice');
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
|
|
return;
|
|
}
|
|
sealed = true;
|
|
this.timings.record('end');
|
|
|
|
var args = arrayProtoSlice.call(arguments);
|
|
|
|
arrayProtoUnshift.call(args, scope);
|
|
|
|
if (err) { // in case it errored, we do not process any thenables
|
|
_.isArray(this._catch) && _.invokeMap(this._catch, _.apply, scope, arguments);
|
|
}
|
|
else {
|
|
// call all the `then` stuff and then the main callback
|
|
_.isArray(this._done) && _.invokeMap(this._done, _.apply, scope, _.tail(arguments));
|
|
}
|
|
|
|
setTimeout(callback.bind.apply(callback, args), 0);
|
|
}.bind(this);
|
|
|
|
// add two additional arguments at the end of the arguments saved - i.e. the payload and a function to call the
|
|
// callback asynchronously
|
|
params.push(this.payload, doneAndSpread);
|
|
|
|
this.timings.record('start');
|
|
|
|
// run the processor in a try block to avoid causing stalled runs
|
|
try {
|
|
this._processor.apply(scope, params);
|
|
}
|
|
catch (e) {
|
|
doneAndSpread(e);
|
|
}
|
|
};
|
|
|
|
Instruction.prototype.done = function (callback) {
|
|
(this._done || (this._done = [])).push(callback);
|
|
|
|
return this;
|
|
};
|
|
|
|
Instruction.prototype.catch = function (callback) {
|
|
(this._catch || (this._catch = [])).push(callback);
|
|
|
|
return this;
|
|
};
|
|
|
|
Instruction.clear = function () {
|
|
_.forEach(Instruction._queue, function (instruction) {
|
|
delete instruction._done;
|
|
});
|
|
Instruction._queue.length = 0;
|
|
};
|
|
|
|
Instruction.shift = function () {
|
|
return Instruction._queue.shift.apply(Instruction._queue, arguments);
|
|
};
|
|
|
|
Instruction.unshift = function () {
|
|
return Instruction._queue.unshift.apply(Instruction._queue, arguments);
|
|
};
|
|
|
|
Instruction.push = function () {
|
|
return Instruction._queue.push.apply(Instruction._queue, arguments);
|
|
};
|
|
|
|
return Instruction;
|
|
};
|
|
|
|
module.exports = {
|
|
pool: pool
|
|
};
|