几年前,我与实验的NodeJS和发现“步骤”库清理我的一些代码,而漂亮。 当希望把这些代码是最新的,我注意到步数“红旗”。 (并非为一两年,只有32个提交等更新)
所以,我环顾四周,发现Async.js ,它有更多的功能和有效的维护。
看起来不错,一般。 但是,我开始尝试应用改造,用它来代替,并且可能不会采取正确的角度。
如果我读正确,步骤的核心功能似乎正是Async.JS会叫“瀑布”的格局 。 因此,在步骤你可以这样写:
Step(
function firstStepNoArgs() {
foo.asyncCall(this);
},
function secondStep(err, argFromFoo) {
if (err) {
handleError(err);
}
bar.asyncCall(argFromFoo, 1, this.parallel());
baz.asyncCall(argFromFoo, 2, this.parallel());
},
function thirdStep(err, argFromBar, argFromBaz) {
if (err) {
handleError(err);
}
/* etc... */
}
);
如果我不知道任何更好,我可能已经猜到你会做,在async.js像这样(未经测试,认为这是伪代码,我说的是我还没有真正追求的理论变化尚)
function thirdStep(argFromBar, argFromBaz) {
/* etc... */
}
async.waterfall([
function firstStepNoArgs(callback) {
foo.asyncCall(callback);
},
function secondStep(argFromFoo, callback) {
async.parallel([
barResult: function(callback) {
bar.asyncCall(parameterFromFoo, 1, callback);
},
bazResult: function(callback) {
baz.asyncCall(parameterFromFoo, 2, callback);
}
],
function(err, result) {
if (err) {
handleError(err);
} else {
thirdStep(result.barResult, result.bazResult);
}
}
}
],
function(err, result) {
if (err) {
handleError(err);
} else {
/* no-op? just assume third-step runs? */
}
}
);
步骤是非常注重和顺序,我这里有点草案显示,它在适应越来越混乱。 我缺少的东西吗?
所以我的问题是 :什么是明确的步骤代码转换成Async.JS的正确方法? 还是我选择了错误的库升级到? 我不希望我的代码变得更丑,但我不想依赖,似乎有种“死”一库,无论是。 : - /
按照要求,什么样的实现您与承诺做。 只需粘贴并运行,你应该明白我的意思。 牢记代码的上半部分是建立模拟功能,这样可以更好地沿着这是如何工作遵循。 有人可能会告诉我,我应该这样做的依据,这是我可以做的一样好。
var Q = require('q');
var foo ={}, bar ={}, baz = {};
// let's mock up some of your objects with some asynch functions
// using setTimeout for async completion
foo.asyncCall = function ( cb) {
setTimeout(function(){ cb(null, 'promises'); },500);
};
bar.asyncCall = function ( arg1, arg2, cb) {
setTimeout(function(){
var result = arg1 + ' can be ' + arg2;
cb(null, result);
},1200);
};
// going to add a will-always-fail function for example purposes
bar.asyncFailure = function (arg1, arg2, cb){
setTimeout(function(){
cb(new Error(arg1 +' offer decent error handling'), null);
},2000); // longer delay - simulate a timeout maybe
};
baz.asyncCall = function ( arg1, arg2, cb) {
setTimeout(function(){
var result = arg1 + ' are really ' + arg2;
cb(null, result);
},800);
};
// set up promise-enbaled calls. Q.denodeify is an easy way to deal with any
// standard node function with a final parameter being an (err,data) callback
// If these are your own functions, you can also create your own promises, but
// Q.nodeify is probably the fastest way to adapt existing code.
bar.promiseFailure = Q.denodeify(bar.asyncFailure);
bar.promiseCall = Q.denodeify(bar.asyncCall);
baz.promiseCall = Q.denodeify(baz.asyncCall);
// this is your wrap up call ('thirdStep' in your code)
function allTogetherNow(arg1, arg2) {
console.log(arg1 +'\n' + arg2);
};
// now we can have some fun
// an example that will run to completion normally
// Q.ninvoke is sort of a 'one-time' denodeify, it invokes a node-style function
// and returns a promise
function example(){
Q.ninvoke(foo,'asyncCall')
.then( function (x) {
return [bar.promiseCall(x, 'confusing at first'),
baz.promiseCall(x, 'awesome after that')]
})
.spread(allTogetherNow)
.fail(function(e){console.log('Had an error')})
.finally(function(){console.log('Calling no matter what from example')});
};
// sometimes things aren't entirely fun though, and there can be an error
function example2(){
Q.ninvoke(foo,'asyncCall')
.then( function (x) {
return [bar.promiseFailure(x, 'confusing at first'),
baz.promiseCall(x, 'awesome after that')]
})
.spread(allTogetherNow)
.fail(function(e){console.log(e)})
.finally(function(){console.log('Calling no matter what from example2')});
};
example();
example2();
对于那些不希望打扰运行它,发出的输出是:
promises can be confusing at first
promises are really awesome after that
Calling no matter what from example
[Error: promises offer decent error handling]
Calling no matter what from example2
小心你的回调,在这里。 回调签名是:
callback(err, arg1, arg2 ...)
所以,如果你foo.asyncCall
是与调用它:
callback(result1, result2)
然后,整个异步会莫名其妙的从该点失败。 成功的正确的回调应该用一个空,如启动
callback(null, result1, result2)
下面是更正后的代码:
function thirdStep(argFromBar, argFromBaz, callback) {
/* etc... */
callback(null, result);
}
async.waterfall([
function firstStepNoArgs(callback) {
// error callback("failed"). First args is not null means failed
// in case of error, it just goes straight to function(err, result)
foo.asyncCall(callback);
},
function secondStep(argFromFoo, callback) {
// argFromFoo come from previous callback (in this case, result1)
async.parallel([
barResult: function(callback) {
bar.asyncCall(parameterFromFoo, 1, callback);
},
bazResult: function(callback) {
baz.asyncCall(parameterFromFoo, 2, callback);
}
],
function(err, result) {
if (err) {
// in case of error you should do callback(error),
// this callback is from secondStep(argFromFoo, callback).
// this will pass to final function(err, result).
handleError(err);
} else {
// you need to do callback(null) inside thirdStep
// if callback is not called, the waterfall won't complete
thirdStep(result.barResult, result.bazResult, callback);
}
}
}
],
function(err, result) {
if (err) {
handleError(err);
} else {
// everything is executed correctly,
// if any step failed it will gone to err.
}
}
);