我有一个点击处理程序,需要一个接一个地进行多个异步调用。我选择使用promises(RSVP来构建这些调用,确切地说)。
下面,您可以在控制器内看到clickA
处理程序(它是一个Ember应用程序,但我认为问题更为笼统):
App.SomeController = Ember.Controller.extend({
actions: {
clickA: function() {
var self = this;
function startProcess() {
return makeAjaxCall(url, {
'foo': self.get('foo')
});
}
function continueProcess(response) {
return makeAjaxCall(url, {
'bar': self.get('bar')
});
}
function finishProcess(response) {
return new Ember.RSVP.Promise(...);
}
...
startProcess()
.then(continueProcess)
.then(finishProcess)
.catch(errorHandler);
}
}
});
它看起来很棒,但现在我必须添加第二个重复使用某些步骤的操作。
由于每个内部函数都需要从控制器访问属性,因此一种解决方案是使它们成为控制器的方法:
App.SomeController = Ember.Controller.extend({
startProcess: function() {
return makeAjaxCall(url, {
'foo': this.get('foo')
});
},
continueProcess: function(response) {
return makeAjaxCall(url, {
'bar': this.get('bar')
});
},
finishProcess: function(response) {
return new Ember.RSVP.Promise(...);
},
actions: {
clickA: function() {
this.startProcess()
.then(jQuery.proxy(this, 'continueProcess'))
.then(jQuery.proxy(this, 'finishProcess'))
.catch(jQuery.proxy(this, 'errorHandler'));
},
clickB: function() {
this.startProcess()
.then(jQuery.proxy(this, 'doSomethingElse'))
.catch(jQuery.proxy(this, 'errorHandler'));
}
}
});
所以,我的问题是:有更好的方法吗?我可以以某种方式摆脱所有jQuery.proxy()
次呼叫吗?
答案 0 :(得分:4)
解决方案是使用更好的诺言库。
Bluebird有一个bind函数,可让您将上下文绑定到整个承诺链(您传递给then
或catch
或finally
的所有函数用这个上下文调用。)
这是一篇关于使用绑定承诺的文章(我写的),就像你想保留一个控制器/资源一样:Using bound promises to ease database querying in node.js
我实现了这样的承诺:// returns a promise bound to a connection, available to issue queries
// The connection must be released using off
exports.on = function(val){
var con = new Con(), resolver = Promise.defer();
pool.connect(function(err, client, done){
if (err) {
resolver.reject(err);
} else {
// the instance of Con embeds the connection
// and the releasing function
con.client = client;
con.done = done;
// val is passed as value in the resolution so that it's available
// in the next step of the promise chain
resolver.resolve(val);
}
});
// the promise is bound to the Con instance and returned
return resolver.promise.bind(con);
}
允许我这样做:
db.on(userId) // get a connection from the pool
.then(db.getUser) // use it to issue an asynchronous query
.then(function(user){ // then, with the result of the query
ui.showUser(user); // do something
}).finally(db.off); // and return the connection to the pool
答案 1 :(得分:3)
我可能会遗漏一些东西,但这会解决你的问题吗?
actions: (function() {
var self = this;
function startProcess() { /* ... */ }
function continueProcess(response) { /* ... */ }
function finishProcess(response) { /* ... */ }
function doSomethingElse(response) { /* ... */ }
/* ... */
return {
clickA: function() {
startProcess()
.then(continueProcess)
.then(finishProcess)
.catch(errorHandler);
},
clickB: function() {
startProcess()
.then(doSomethingElse)
.catch(errorHandler));
}
};
}());
只需将actions
包装在IIFE中,并将常用功能存储在那里,只显示您需要的最终功能。但我根本不认识Ember,也许我错过了一些基本的东西......
答案 2 :(得分:2)
浏览器对所有函数都有“绑定”方法。为Function#bind创建一个pollyfill也很容易。
this.startProcess()
.then(this.continueProcess.bind(this))
.then(this.finishProcess.bind(this))
.catch(this.errorHandler.bind(this));
jQuery.proxy
方法基本上做同样的事情。