我想在Node.js中创建一系列异步方法,例如:
function functionA(data, callback){
console.log("A")
// do something here
callback();
}
function functionB(data, callback){
console.log("B");
// do something here
callback();
}
function functionC(data, callback){
console.log("C");
// do something here
callback();
}
每个函数都是独立的,但是当链接时,它们可以被有序地调用。例如:
functionA(data).functionC(data).functionB(data)
将打印A
然后C
然后B
。连锁店的订单可以不受限制地重新安排。
这是我搜索过的内容:
我的理由async
不是我期望的答案。
考虑会计问题,如果有一笔总额为1200美元的卖出交易,首先我必须将1200美元投入资产:现金簿(借方),然后我必须将1200美元存入收入账户(信用方)。是否可以使用异步?当然是。它看起来像这样:
async.series([
function(callback){
bookLibrary.debit('cash', 1200, callback);
},
function(callback){
bookLibrary.credit('income', 1200, callback);
}
]);
所以我想通过链接它们来简化和更容易阅读,就像这样:
bookLibrary.debit('cash', 1200).credit('income', 1200)
答案 0 :(得分:3)
首先,要链接一堆函数,您需要将它们配置为公共对象上的方法。然后,每个方法都可以返回该对象,因此返回结果是对象,并且可以在该对象上调用下一个方法。
所以,做这样的事情:
a().b().c();
您需要a()
将具有方法b()
的对象作为属性返回。而且,类似地,您需要a().b()
返回一个对象c()
作为属性。这通常称为Fluent界面。对于同步方法而言,这一切都非常简单。这正是jQuery如何进行链接的。
$(".foo").show().css("color", "blue");
所有这三个调用都返回一个jQuery对象,而jQuery对象包含你可以链接的所有方法。
在上面的示例中,您可以像这样进行同步链接:
function a() {
}
a.prototype = {
b: function() {
// do something
return this;
},
c: function() {
// do something else
return this;
}
};
但是,你的问题是关于异步操作。这是更多的工作,因为当你这样做:
a().b().c();
这将一个接一个地立即执行所有三种方法,并且不会等待它们中的任何一种完成。使用这种确切的语法,我知道支持链接的唯一方法是构建一个队列,而不是立即实际执行.b(xxx)
,该对象将该操作排队,直到a()
完成。这就是jQuery如下所示的动画:
$(".foo").slideUp().slideDown();
因此,从每个方法返回的对象可以包含一个队列,当一个操作完成时,该对象然后从队列中提取下一个项目,分配它的参数(也保存在队列中),执行它和监视该异步操作,它再次从队列中提取下一个项目。
这是队列的一般概念。当我进入这个实现时,我意识到承诺会使这更容易。以下是不使用promises(未经测试)的实现的一般概念:
为了简化异步操作的示例,让make a()
执行10ms setTimeout,.b()
执行50ms setTimeout,.c()
执行100ms setTimeout。实际上,这些可以是完成后调用回调的任何异步操作。
function a() {
if (!(this instanceof a)) {
return new a();
} else {
this.queue = [];
this.inProgress = false;
this.add(function(callback) {
// here our sample 10ms async operation
setTimeout(function() {
callback(null);
}, 10);
}, arguments);
}
}
a.prototype = {
b: function() {
this.add(function(callback) {
// here our sample 50ms async operation
setTimeout(function() {
callback(null);
}, 50);
return this;
}, arguments);
},
c: function(t) {
this.add(function(t, callback) {
// here our sample 100ms async operation
setTimeout(function() {
callback(null);
}, t);
return this;
}, arguments);
},
add: function(fn, args) {
// make copy of args
var savedArgs = Array.prototype.slice.call(args);
this.queue.push({fn: fn, args:savedArgs});
this._next();
},
_next: function() {
// execute the next item in the queue if one not already running
var item;
if (!this.inProgress && this.queue.length) {
this.inProgress = true;
item = this.queue.shift();
// add custom callback to end of args
item.args.push(function(err) {
this.inProgress = false;
if (err) {
// clear queue and stop execution on an error
this.queue = [];
} else {
// otherwise go to next queued operation
this._next();
}
});
try {
item.fn.apply(this, item.args);
} catch(e) {
// stop on error
this.queue = [];
this.inProgress = false;
}
}
}
};
// usage
a().b().c(100);
如果我们对异步操作和排队使用promises,那么事情会变得更简单:
此处的所有异步操作(例如firstAsyncOperation
和secondAsyncOperation
都会返回一个大大简化事物的承诺。异步链接由promise基础结构为我们完成。
function a(arg1, arg2) {
if (!(this instanceof a)) {
return new a(arg1, arg2);
} else {
this.p = firstAsyncOperation(arg1, arg2);
}
}
a.prototype = {
b: function() {
return this._chain(secondAsyncOperation, arguments);
},
c: function() {
return this._chain(thirdAsyncOperation, arguments);
},
_chain: function(fn, args) {
var savedArgs = Array.prototype.slice.call(args);
this.p = this.p.then(function() {
return fn.apply(this, savedArgs);
});
return this;
},
then: function(a, b) {
this.p = this.p.then(a, b);
return this;
},
catch: function(fn) {
this.p = this.p.catch(fn);
return this;
}
};
// usage:
a().b().c(100).then(function() {
// done here
}).catch(function() {
// error here
});
答案 1 :(得分:1)
您可以使用async waterfall来做到这一点,这应该符合您的要求。
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
答案 2 :(得分:-1)
您可以将所有功能都包含在一个对象中,如下所示。
var Ext = {
function a() {
return this;
}
function b() {
return this;
}
}
然后您可以按以下方式调用它们
Ext.a().b();
详细示例请查看我的javascript库代码,它完全符合您的需求https://github.com/waqaskhan540/MapperJs