Brendan Annable
Brendan Annable

Reputation: 2747

Bluebird Promise Scope

I have just started using promises in attempt to cleanup some 'callback hell'.

I've decided on trying bluebird and I am running it in the browser but immediately ran into scoping problems.

Is there a way of setting the thisArg in a new Promise? The below example shows that the 'this' value inside the promise resolver is set to the browser window, but I'd like it set to the surrounding scope so I can easily access member variables.

I noticed there is a .bind() method but it only scopes the 'then()' method, not the promise. I also realize I can have 'var me = this' just before the promise and use closure, but I wanted to avoid it if possible.

function MyObject() {
    this.value = 7;
}

MyObject.prototype.getValue = function () {
    return new Promise(function (resolve) {
        // some request/processing that takes a long time
        var result = Ajax.request(...);

        resolve({
            value: this.value,
            result: result
        });
        // 'this' is set to window instead of the instance,
        // resulting in this.value as undefined
    });
}

var obj = new MyObject();
obj.getValue().then(function (value) {
    console.log(value); // preferably outputs 7
})

Upvotes: 1

Views: 1590

Answers (2)

t.niese
t.niese

Reputation: 40842

This is more a comment then an answer, as it is primary opinion based.

In the rar situations where I need this it would look like this in my code:

Ajax.requestAsync in this case would be a promisifyed version of Ajax.request.

I know this might just move your problem to another place.

MyObject.prototype.getValue = function () {
    return Ajax.requestAsync(...)
    .bind(this)
    .then(function(result) {
        return {
              value: this.value,
              result: result
            }
    });
}

Or something like this:

 MyObject.prototype.getValue = function () {
    var returnValue = {
        value: this.value
    };

    return Ajax.requestAsync(...)
    .then(function(result) {
        returnValue.result = result;
        return returnValue;
    });
}

A rarely use such constructs:

MyObject.prototype.getValue = function () {
  return Promise.all([this, Ajax.requestAsync(...)])
  .spread(function(object, result) {
      return {
        value: object.value,
        result: result
      };
  });
}

Upvotes: 2

Bergi
Bergi

Reputation: 664346

No, there is not. You can of course use the default approaches, but you shouldn't need to.

When doing heavy processing and getting back the value asynchronously, you want to get a promise for the value. You don't need to set the result value as a property of the original instance.

MyObject.prototype.getValue = function () {
    return new Promise(function(resolve) {
        // lots of processing to make a `value`
        resolve(value); // no `this` at all!
    });
};

In case you want to synchronously get the .value that you had set on the instance, you don't need the Promise constructor. Just use Promise.resolve to make a promise for an existing value:

MyObject.prototype.getValue = function () {
    // TODO: lots of processing
    return Promise.resolve(this.value);
};

Or, in your case, even Promise.method:

// TODO: lots of processing
MyObject.prototype.getValue = Promise.method(function () {
    return this.value;
});

Upvotes: 2

Related Questions