Combining promises and chaining

2019-03-04 18:58发布

问题:

Is there a good design pattern for utilizing promises, while still supporting chaining? For example, let's say that we have something like:

function Foobar() {
  this.foo = function() {
    console.log('foo');
    return this;
  };

  this.bar = function () {
    console.log('bar');
    return this;
  };
}

var foobar = new Foobar();
foobar.foo().bar();

If instead we change the methods to use promises, e.g.

function Foobar() {
  this.foo = function() {
    var self = this;
    return new Promise(function (resolve, reject) {
      console.log('foo');
      resolve(self);
    });
  };
  ...
}

Is there a good way to do something like:

var foobar = new Foobar();
foobar.foo().bar().then(function () {
  // do something
});

回答1:

If instead we change the methods to use promises, e.g.

this.foo = function() {
  var self = this;
  return new Promise(function (resolve, reject) {
    …
    resolve(self);
  });
};

I would recommend not to do that. This just begs for trouble. If your instance (or: its modification) is the result of your computation, then what is the state of your object (I assume you use OOP to encapsulate state) during that computation? What happened if this (or any other) method is called from a different process (thread) turn of the event loop? You'd have to go down the entire rabbit hole…

Try to use functional programming as far as you can.

Is there a good way to do something like:

new Foobar().foo().bar().then(function () {
  // do something
});

Yes, but you'd have to use .then after every invocation:

new Foobar().foo().then(function(foobar) {
  return foobar.bar();
}).then(function(barresult) {
  // do something
});

The Bluebird library (as do some others) even has a utility function for that .call(). So you'd do

new Foobar().foo().call("bar").then(function(barresult) {
  // do something
});


回答2:

Just use then.

function Foobar() {
  this.foo = function() {
    var self = this;
    return new Promise(function (resolve, reject) {
      console.log('foo');
      resolve(self);
    });
  };
  this.bar = function() {
    var self = this;
    return new Promise(function (resolve, reject) {
      console.log('bar');
      resolve(self);
    });
  }
}

new Foobar().foo().then(function(foobar) { return foobar.bar(); });


回答3:

There is a handy .call method that allows you to call a method on the resolved value. I've found it pretty handy in a few cases: https://github.com/petkaantonov/bluebird/blob/master/API.md#callstring-propertyname--dynamic-arg---promise