在javascript中使用promise和原型时,优雅的回调绑定
Elegant callback binding when using promises and prototypes in javascript
我是一个重度javascript原型和promise用户。
我的问题是,每当我履行承诺时,我都需要使用.bind(this)
来设置正确的上下文。
以下是显示问题的示例代码(jsbin):
var Q = require('Q');
var AsyncCounter = function(initialValue){
this.value = initialValue;
};
AsyncCounter.prototype.increment=function(){
return Q.fcall(function(){
return ++this.value;
}.bind(this));
};
AsyncCounter.prototype.decrement=function(){
return Q.fcall(function(){
return --this.value;
}.bind(this));
};
var counter = new AsyncCounter(10);
counter.increment()
.then(function(incrementedValue){
console.log('incremented value:', incrementedValue)
})
.then(counter.decrement.bind(counter))
.then(function(decrementedValue){
console.log('decremented value:', decrementedValue)
});
看看我需要多久依赖一次bind()
?我觉得这太不合法了。
我确实知道petkaantonov/bluebird promise库及其在回调上传播作用域的非常有用的bind(scope)
函数,但我觉得有一种更好的原生方法可以做到这一点。
有人有合适的方法吗?
还记得当您学习了Foo.prototype.bar=function(){...}
并放弃将构造函数内的方法定义为this.bar = function(){...}
时吗?原因是速度和内存的效率。
现在你有充分的理由让时间倒流,牺牲速度/效率来实现"可分离"的方法,即特定于具有this
就绪绑定的构造函数的实例的方法,这正是你想要的。
如果多次调用这些方法,那么在引用函数时不必使用bind()
,将大大弥补构造的低效性。此外,你还可以获得你所寻求的句法上的便利。
以下是您的示例的一个版本,该版本经过了重新调整,以更好地展示语法上的便利性:
var AsyncCounter = function(value){
this.increment = function(){
return Q.fcall(function(){
return console.log("++value: " + ++value);
}.bind(this));
}.bind(this);
this.decrement = function(){
return Q.fcall(function(){
return console.log("--value: " + --value);
}.bind(this));
}.bind(this);
};
var counter = new AsyncCounter(10);
counter.increment()
.then(counter.decrement)
.then(counter.increment);
总之,您需要做两件事:
- 在构造函数内定义方法
- 将CCD_ 8与方法结合
我认为另一种方法是利用闭包。promise中使用的每个回调都可以包含对创建它的根作用域的一些变量的引用。
通常,大多数人所做的是创建一个名为self
的变量,该变量主要引用外部this
。因此,无论您在哪里执行promise的函数,闭包都将维护引用,从而能够访问范围。
AsyncCounter.prototype.increment=function(){
var self = this;
return Q.fcall(function(){
return ++self .value;
});
};
老实说,我不确定哪一个更优雅。我不确定是否有其他方法可以在不使用此技术或bind
方法的情况下完成您想要的操作。
在我看来,最好的解决方案是添加一个context
方法来承诺在未来的then
调用中为函数调用指定this
。这似乎是我在一些库中看到的,不记得在哪里,并且在我自己的库中实现的。这个想法是写
promise.context(this).then(myfunc)
我不知道Q,所以我不知道扩展它/自定义它以实现这种行为是否容易,甚至可能。以下是这个想法的玩具实现,使用原生JS promise:
Promise.prototype.context = function (ctxt) { this.ctxt = ctxt; return this; }
Promise.prototype.then = (function() {
var old_then = Promise.prototype.then;
return function(fulfill, reject) {
return old_then.call(this,
fulfill && fulfill.bind && fulfill.bind(this.ctxt),
reject && reject.bind && reject.bind(this.ctxt)
);
};
}());
实际上,您可以经常编写代码,这样就不会出现context
调用,只需在创建promise的地方执行一次即可。因此:
var MyTrip = {
gotoMars: function() { return InterPlanetaryTrip(Mars).context(this); },
report: function() { console.log("I'm back, Mom!"); },
go: function() { gotoMars.then(this.report).then(this.drink); }
};
这种方法省去了我无限的烦恼,而且我看不到任何负面影响。无论这意味着什么,我都看不到比这更"本土"的方式了。
侵入性较小的方法
作为对你的问题的更直接的回答,你可以引入一点糖——一种一次性绑定并调用Q.fcall
的方法,如下所示:
AsyncCounter.prototype.fcall = function(fn) {
return Q.fcall(fn.bind(this));
};
AsyncCounter.prototype.increment = function() {
return this.fcall(function() {
return ++this.value;
});
};
如果使用ES6,可以使用箭头函数和词法范围。
AsyncCounter.prototype.increment = () => {
return Q.fcall(() => ++this.value);
};
- 为什么prototypjs观察到回调函数有绑定
- 在Backbone.js中将回调绑定到此
- 将web服务回调的结果绑定到Javascript中的调用函数
- 将回调绑定到没有隔离作用域的指令
- 将其绑定在 backbone.js 中的 jQuery 回调中
- 将类方法绑定到类外绑定的事件处理程序内的AJAX成功回调
- KnockoutJS在绑定完成后触发回调
- 使用闭包/函数绑定将自函数作为回调传递
- 当通过Ajax成功回调更新可观察数组时,启用绑定中断
- 异步加载ASP.NET绑定的Javascript,然后调用回调
- 绑定promise回调函数的“this”范围
- 在呈现项目后,挖空选择绑定回调的选项
- 主干 - 使用嵌套的 json 数据将表单提交绑定回模型
- 无法访问“”;这个“;backbone.js应用程序中的绑定回调
- 如何在可观察数组中正确地将文本框值绑定回模型
- 异步范围绑定回调中的JavaScript作用域操作不适用
- 将$(this)传递给绑定回调函数
- 如何在整个jstree重载事件上绑定回调函数
- jQuery,绑定回调到任何函数
- 绑定回调参数