Skip to content

Instantly share code, notes, and snippets.

@bang590
Created October 22, 2012 09:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bang590/3930621 to your computer and use it in GitHub Desktop.
Save bang590/3930621 to your computer and use it in GitHub Desktop.
stepEngine - simplify everyauth asynchronous resolution
var fs = require('fs'),
http = require('http');
var Promise = function(values) {
this._callbacks = [];
this._errbacks = [];
if (arguments.length > 0) {
this.fulfill.apply(this, values);
}
}
Promise.prototype = {
callback: function(fn, scope) {
//已有values表示promise已fulfill,立即执行
if (this.values) {
fn.apply(scope, this.values);
return this;
}
this._callbacks.push([fn, scope]);
return this;
}
, errback: function(fn, scope) {
if (this.err) {
fn.call(scope, this.err);
return this;
}
this._errbacks.push([fn, scope]);
return this;
}
, fulfill: function () {
if (this.isFulfilled || this.err) return;
this.isFulfilled = true;
var callbacks = this._callbacks;
this.values = arguments;
for (var i = 0, l = callbacks.length; i < l; i++) {
callbacks[i][0].apply(callbacks[i][1], arguments);
}
return this;
}
, fail: function (err) {
var errbacks = this._errbacks;
for (var i = 0, l = errbacks.length; i < l; i++) {
errbacks[i][0].call(errbacks[i][1], err);
}
return this;
}
}
var Step = function(name, _engine) {
this.name = name;
this.engine = _engine;
}
Step.prototype = {
exec : function(seq) {
var args = this._unwrapArgs(seq)
, promises = this.promises
var ret = this.engine[this.name]().apply(this.engine, args);
//若函数返回不是Promise,即是函数里直接返回值无异步操作。为流程一致,包装一个立即执行的promise
ret = (ret instanceof Promise)
? ret
: this.engine.Promise([ret]);
ret.callback(function() {
var returned = arguments
, vals = seq.values;
//step执行结束后把返回值写入seq.values供下一个step使用
if (promises !== null) promises.forEach( function (valName, i) {
vals[valName] = returned[i];
});
})
//加上默认的错误回调方法
ret.errback(this.engine.errback(), this.engine);
return ret;
}
, _unwrapArgs: function (seq) {
if (!this.accepts) return [];
return this.accepts.reduce( function (args, accept) {
//根据accept名取出对应变量
args.push(seq.values[accept]);
return args;
}, []);
}
}
var Sequence = function(name, engine) {
this.name = name;
this.engine = engine;
this.stepNames = [];
this.values = {};
}
Sequence.prototype = {
_bind : function(priorPromise, nextStep) {
var nextPromise = new Promise()
, seq = this;
priorPromise.callback( function () {
var resultPromise = nextStep.exec(seq);
resultPromise.callback(function () {
nextPromise.fulfill();
});
});
return nextPromise;
}
, start : function() {
var steps = this.steps;
var priorStepPromise = steps[0].exec(this);
for (var i = 1, l = steps.length; i < l; i++) {
//绑定step链
priorStepPromise = this._bind(priorStepPromise, steps[i]);
}
return priorStepPromise;
}
}
Object.defineProperty(Sequence.prototype, 'steps', {
get: function () {
var allSteps = this.engine._steps;
return this.stepNames.map(function (stepName) {
return allSteps[stepName];
})
}
});
var engine = {
configurable : function(name) {
this[name] = function(setTo) {
var k = '_' + name;
if (arguments.length) {
this[k] = setTo;
return this;
}
return this[k];
}
return this;
}
, step : function(name) {
var steps = this._steps
, sequence = this._currSeq;
sequence.stepNames.push(name);
this._currentStep =
steps[name] || (steps[name] = new Step(name, this));
this.configurable(name);
return this;
}
, accepts : function(input) {
this._currentStep.accepts = input && input.split(/\s+/) || null;
return this;
}
, promises : function(output) {
this._currentStep.promises = output && output.split(/\s+/) || null;
return this;
}
, do : function(name) {
this._currSeq =
this._stepSequences[name] || (this._stepSequences[name] = new Sequence(name, this));
return this;
}
, Promise : function(values) {
return values ? new Promise(values) : new Promise();
}
, _stepSequences: {}
, _steps: {}
, start : function(seqName) {
var seq = this._stepSequences[seqName];
seq.start();
}
}
engine
.configurable('errback')
.errback(function(err) {
console.log('errback', err);
});
engine
.do('fetchHtml')
.step('readUrl')
.accepts('')
.promises('url')
.step('getHtml')
.accepts('url')
.promises('html')
.step('saveHtml')
.accepts('url html')
.promises(null)
.readUrl(function(){
var p = this.Promise();
//url.txt保存了一个网址
fs.readFile('./url.txt', 'utf8', function (err,data) {
if (err) p.fail(err);
else p.fulfill(data);
});
return p;
})
.getHtml(function(url){
var p = this.Promise();
http.get(url, function(res){
var body = '';
res.on('data', function(c){
body += c;
}).on('end', function(){
p.fulfill(body);
});
}).on('error', function(err){
p.fail(err)
});
return p;
})
.saveHtml(function(url, html){
fs.writeFile('./fetchResult', url + html, function(e) {
if (e) console.log('error', e);
else console.log('done');
});
})
.start('fetchHtml')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment