我有一个点击处理程序,需要一个接一个地进行多次异步调用。我选择使用 promise(准确地说是 RSVP )来构建这些调用。

下面,您可以看到 Controller 内部的 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);
    }
  }
});

看起来不错,但现在我必须添加第二个操作来重用某些步骤。

由于每个内部函数都需要从 Controller 访问属性,因此一种解决方案是使它们成为 Controller 的方法:
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() 调用吗?

最佳答案

一个解决方案是使用更好的 promise 库。

Bluebird 有一个 bind 函数,它允许您将上下文绑定(bind)到整个 promise 链(您传递给 thencatchfinally 的所有函数都使用此上下文调用)。

这是一篇(我写的)关于像你想保留 Controller /资源一样使用绑定(bind) promise 的文章:Using bound promises to ease database querying in node.js

我这样建立我的 promise :

// 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

关于javascript - 链式 promise 和保留 `this`,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21315817/

10-16 19:54