通过在客户端上调用多个 Meteor 方法避免回调地狱

Avoiding Callback Hell with Multiple Meteor Method calls on Client

我有多个 Meteor.calls,其中每个方法都取决于另一个 Meteor 方法的响应。

客户

Meteor.call('methodOne', function(err, resOne){
    if(!err){
        Meteor.call('methodTwo', resOne, function(err, resTwo){
            if(!err){
                Meteor.call('methodThree', resTwo, function(err, resThree){
                    if(err){
                        console.log(err);
                    }
                })
            }
        });
    }
});

我从 Meteor 的文档中了解到

"Methods called on the client run asynchronously, so you need to pass a callback in order to observe the result of the call."

我知道我可以在服务器上创建另一个 Meteor Method 来执行方法 'methodOne'、'MethodTwo'、'MethodThree' 使用 Meteor.async 包装,或者在没有回调的情况下顺序执行全部一起。但我担心这条路径会导致我的流星方法变得臃肿和纠缠,导致意大利面条代码。我宁愿通过一项工作来保持每个 Meteor 方法的简单性,并找到一种更优雅的方式来链接客户端上的调用。任何想法,有没有办法在客户端使用 Promises?

编辑:您最好看看@Benjamin Gruenbaum 的回答,这不仅会带来更好的性能,还会提供更简洁的代码。

承诺 - 是的。

我喜欢RSVP very much, why? Simply because it's the fastest one. (quick benchmark: jsperf)。

下面是您的代码的快速重写:

var promise = new RSVP.Promise(function(fulfill, reject) {
  Meteor.call('methodOne', '', function(err, resOne) {
    if (!err) {
      return reject(err);
    }
    fulfill(resOne);
  });
});

promise.then(function(resOne) {
  return new RSVP.Promise(function(fulfill, reject) {
    Meteor.call('methodTwo', resOne, function(err, resTwo) {
      if (err) {
        return reject(err);
      }
      fulfill(resTwo);
    });
  });
}).then(function(resTwo) {
  return new RSVP.Promise(function(fulfill, reject) {
    Meteor.call('methodTwo', resTwo, function(err, resThree) {
      if (err) {
        reject(err);
      }
      fulfill(resThree);
    });
  });
}).then(function(resThree) {
  // resThree is available - continue as you like
  console.log(resThree);
}).catch(function(err) {
  console.log(err);
});

这就是防止 "the ever rightward drift" 代码的方法。

Promise 很酷,使用它们。

由于其他答案建议 RSVP,此答案将建议 Bluebird which is actually the fastest promise library when running real benchmarks. Rather than a micro benchmark 并没有真正衡量任何有意义的东西。无论如何,我不是为了性能而选择它,我在这里选择它是因为它也是最容易使用且具有最佳可调试性的。

与其他答案不同,这个答案也不会抑制错误,并且使函数 return 成为承诺的成本是微不足道的,因为没有调用承诺构造函数。

var call = Promise.promisify(Meteor.call, Meteor);

var calls = call("methodOne").
            then(call.bind(Meteor, "methodTwo")).
            then(call.bind(Meteor, "methodThree"));

calls.then(function(resThree){
    console.log("Got Response!", resThree);
}).catch(function(err){
    console.log("Got Error", err); 
});

您在客户端的方法导致服务器和浏览器之间的往返次数增加很多。我知道您表示您担心服务器上的意大利面条代码,而且我无法像您一样了解您的应用程序,但仅按照您提供的示例,它似乎是将所有三个调用包装在服务器,只从客户端拨打一个电话,恕我直言。