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