node-task / spec

A specification for javascript tasks.
MIT License
153 stars 2 forks source link

Rework on the previous version spec. #21

Open yuanyan opened 10 years ago

yuanyan commented 10 years ago

Current spec is more simple, some work on previous version:

var Promise = require('es6-promise').Promise;
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var _ = require('lodash');

var Status = {
    PENDING:  0, // Indicates that the task has not been executed yet.
    RUNNING:  1, // Indicates that the task is running.
    REJECTED: 2, // Indicates that the task has completed without errors.
    RESOLVED: 3, // Indicates that the task not completed.
    FINISHED: 4, // Indicates that the task has finished.
    FAULTED: 5   // Indicates that the task finished due to an unhandled exception.
};

function Task (config) {
    if (!(this instanceof Task)) return new Task(config);
    this.setOptions(config.options);
    this._status = Status.PENDING;
    _.extend(this, config);
    EventEmitter.call(this);
}

util.inherits(Task, EventEmitter);

Task.Status = Status;

/**
 * Task name must be a combination of one or more digits, letters a–z, underscores, and/or dashes,
 * satisfying the following regular expression: [0-9a-zA-Z\-\_]+
 * @type {String}
 */
Task.prototype.name = null;
Task.prototype.timeout = null;
Task.prototype.force = false;
Task.prototype.options = {};

Task.prototype._parseOptions = function (options) {
    var defaults = _.merge({}, this.options, options, function(d, o) {
        return o.defaultValue || o;
    });
    return _.extend(defaults, this._options || {});
};

Task.prototype.getOptions = function(){
    return this._options;
};

Task.prototype.setOptions = function(options){
    return this._options = this._parseOptions(options);
}

Task.prototype.getStatus = function(){
    return this._status;
};

Task.prototype.getInput = function(){
    return this._input;
};

Task.prototype.setTimeout = function(msecs, callback) {
    if (msecs > 0 && !isNaN(msecs) && isFinite(msecs)) {
        this.timeout = msecs;
        if (callback) {
            this.on('timeout', callback);
            this._timeoutHandler = callback;
        }
    } else if (msecs === 0) {
        this.clearTimeout(callback);
    }
};

Task.prototype.clearTimeout = function(callback){
    this.timeout = null;
    if (this._timer) {
        clearTimeout(this._timer);
    }
    callback = callback || this._timeoutHandler;
    if (callback) {
        this.removeListener('timeout', callback);
    }
};

/**
 * Main entry point for task.
 * @param input
 * @returns {*}
 */
Task.prototype.run = function (input) {
    this._input = !_.isArray(input)? [input]: input;
    return this._run();

};

Task.prototype._run = function(resolve, reject){
    var self = this;
    if(this.timeout){
        this._timer = setTimeout(function(){
            self.emit('timeout');
        }, this.timeout)
    }
    var promise = Promise.resolve();
    return promise.then(this._setup.bind(self))
        .then(this._execute.bind(self))
        .then(this._teardown.bind(self))
        .catch(this._error.bind(self));
}

/**
 * Thenable
 */
Task.prototype.then = function(resolve, reject){
    return this._run(resolve, reject);
};

Task.prototype._setup = function () {
    var self = this;
    return new Promise(function (resolve, reject) {
        self.emit('setup');
        self.setup(resolve, reject);
    });
};

Task.prototype._execute = function(){
    var self = this;
    return new Promise(function (resolve, reject) {
        self.emit('execute');
        self._status = Status.RUNNING;
        self.execute(function(){
            self._status = Status.RESOLVED;
            resolve.apply(self, arguments);
        }, function(){
            self._status = Status.REJECTED;
            reject.apply(self. arguments);
        });
    });
};

Task.prototype._teardown = function(res){
    this.emit('teardown');
    this._status = Status.FINISHED;
    return Promise.resolve(this.teardown(res));
};

Task.prototype._error = function(err){
    this._status = Status.FAULTED;
    this.emit('error', err);
    return this.error(err);
}

/*
When an task is run, the task goes through 3 steps:
1. setup(Params...), invoked before the task is executed. This step is normally used to setup the task.
2. execute(Params...), invoked immediately after setup() finishes executing.
   This step is used to perform task that can take a long time.The parameters of the task are passed to this step.
   The result of the task must be returned by this step and will be passed back to the last step.
3. teardown(Result), invoked after execute() finishes. The result of the execute() is passed to this step as a parameter.
*/

Task.prototype.setup = function (resolve, reject) {
    resolve();
};

Task.prototype.execute = function (resolve, reject) {
    // var input = this.getInput();
    // input.isStream();
};

Task.prototype.teardown = function (res) {
    return res;
};

Task.prototype.error = function (err) {
    return err;
};

module.exports = Task;