-
-
Save lyuehh/3974219 to your computer and use it in GitHub Desktop.
stepEngine - simplify everyauth asynchronous resolution
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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