在Promise回调函数(then)中访问对象的'this'属性

8

我想在Javascript中创建一个对象。

其中一个方法应该执行一个Promise链。链中的每个方法都必须访问对象的成员变量config。 问题是,PromiseMethod2中的this运算符被更改了,我无法访问config变量(在PromiseMethod1中它可以正常工作)。

这是我的代码:

var SomeObject(config) {
    var that = this;
    that.config = config;
}

SomeObject.prototype.SomeMethod = function() {
    var that = this;

    that.PromiseMethod1()
      .then(that.PromiseMethod2)
      .catch(console.error);
    }

SomeObject.prototype.PromiseMethod1 = function() {
    var that = this;
    config = that.config;

    return SomePromise();
}

SomeObject.prototype.PromiseMethod2 = function(someParams) {
    var that = this;
    config = that.config;
    params = someParams;

    return SomePromise();
}


var someObject = new SomeObject(someConfig);
someObject.SomeMethod().then(function () {
    console.log('Done!');
}

我希望使用委托方法来替代直接执行链中的方法:
 that.PromiseMethod1().then(function(response) { return that.PromiseMethod2(that, response); };

我无法使用 bind 方法,因为它似乎在回调函数执行时被重新绑定。

有解决方法吗? 为什么 PromiseMethod1PromiseMethod2 之间存在差异?


你怎么解决SomePromise - Kiril
每个方法可能会做不同的事情。我大多数时候使用'new Promise(resolve,reject)',然后resolve(somePrams)。或者我使用第三方Promise,比如ElasticSearch或Request-Promise。 - shudima
你断言不能使用 bind,但是你试过了吗?看起来它是正确的解决方案。 - Roamer-1888
嗨。是的,我试过了。不起作用。 - shudima
请展示您尝试过的内容。 - Roamer-1888
2个回答

1

我的真正建议是不要使用thisnew(如果你仍然想要继承,可以使用Object.create):

var SomeObject = function(config) {
    return {
        PromiseMethod1: function(){
            return somePromise(config.foo);
        },
        PromiseMethod2: function(x){
            return someOtherPromise(config.bar, x);
        }
    }
}

var instance = SomeObject({config: true});
instance.PromiseMethod1().then(instance.PromiseMethod2);

我在这里使用闭包及其封闭父级词法范围变量的能力,使其发挥作用。与其依靠 JavaScript 在运行时根据调用函数的对象来神奇地注入 this,因为正如您所遇到的问题所示,这并不总是奏效,我宁愿采用这种非常规的方式。
但是,如果您更喜欢使用 this,则需要使用 bind 来告诉 JavaScript 函数属于哪个神奇的 this 值。
var SomeObject function(config) {
    this.config = config;
}

SomeObject.prototype.PromiseMethod1 = function(){
    return somePromise(this.config.foo);
}

SomeObject.prototype.PromiseMethod1 = function(x){
    return someOtherPromise(this.config.bar, x);
}

var instance = new SomeObject({config: true});
instance.PromiseMethod1().then(instance.PromiseMethod2.bind(instance)); //<- :(

在你的示例SomeMethod中,你实际上没有使用bind。你仍然需要绑定,因为你将函数传递给.then(f),接收函数的代码不再知道应该使用哪个对象来调用this。现在再看一下我之前推荐的代码。那里面没有this,所以这些函数完全不依赖于它们被调用的对象是哪个,你可以将它们作为高阶函数传递多次,而无需进行bindthat = this的操作。 :)

0

我认为这是不可能的。你试图混合两种不同的方法:方法链Promise链。我建议您重新审查您的架构。

唯一可见的事情(但个人不喜欢)是,如果您完全控制要使用的所有Promise,则通过整个Promise链传递配置值。

SomeObject.prototype.init = function() {
    var that = this;

    return new Promise(function(resolve, reject) {
        resolve(that.config)
    });
}
SomeObject.prototype.PromiseMethod1 = function(config, params) {
    return SomePromise(config, params);
}

SomeObject.prototype.PromiseMethod2 = function(config, someParams) {  
    return SomePromise(config, someParams);
}

SomePromise = function(config, params) {
    return new Promise(function(resolve, reject) {
        //some code here
        resolve(config, newParamsFromSomeCode)
    });
}

然后您就可以调用:

that.init().then(that.PromiseMethod1).then(that.PromiseMethod2);

但是再次强调,这看起来不像是好的代码...


没有方法链。所有的方法都在SomeObject下面。我尝试了这种方法,问题是Promise resolve方法不支持超过1个参数。 - shudima
是的,但您想逐个调用同一对象的方法。如果您确实需要异步代码-我建议将异步操作放入单独的对象/层/组件/等中,并使用简单的Promise链接。 - Kiril

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接