【问题标题】:Avoiding Callback Hell with Multiple Meteor Method calls on Client通过客户端上的多个 Meteor 方法调用避免回调地狱
【发布时间】:2015-02-20 16:01:21
【问题描述】:

我有多个 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 的文档中我知道

“客户端调用的方法是异步运行的,所以需要通过回调来观察调用的结果。”

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

【问题讨论】:

    标签: javascript asynchronous meteor promise


    【解决方案1】:

    由于其他答案建议 RSVP,因此此答案将建议 Bluebird,这实际上是运行 real benchmarks 时最快的承诺库。而不是 a micro benchmark 这并没有真正衡量任何有意义的事情。无论如何,我不是为了性能而选择它,我在这里选择它是因为它也是最容易使用并且具有最佳可调试性的一种。

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

    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); 
    });
    

    【讨论】:

    • 老实说,我学到了一些东西——我从来没有真正接触过承诺和/或链接承诺,但如果不仅仅是非常简单的案例发挥作用,bluebird 确​​实似乎是最好的选择,而不是只有性能明智。谢谢!
    • @benjamin 可以在服务器端方法调用中使用光纤吗?我正在做外部 http.get 调用并让纤维返回一个承诺。
    • @JasonCochran 当然,它会起作用-尽管“更多流星”的方式是Meteor.wrapAsync,并像同步一样使用代码。对此的“承诺响应”是使用生成器和协程(Bluebird.coroutine)。就我个人而言,我更喜欢 promises 版本,但两者都是可行的(就像普通的回调一样)。只是为了更加清楚 - 它会起作用。
    • 我接受这个作为一个警告的答案,就像你指出 Meteor.wrapAsync 是管理服务器上多个异步函数的“流星方式”一样,我希望有一个流星包或在客户端而不是通用库上管理 promise 的特定于流星的方式。
    【解决方案2】:

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

    【讨论】:

    • +1,我认为这不是这个问题的答案,但在做了更多的工作和研究之后,我同意这是结合强大的设计模式来管理你所有的最佳实践应用流星方法。
    • 谢谢。但是,我确实认为它回答了你的问题。它确实“避免了回调地狱”,并且是您展示的示例代码的最佳设计。
    【解决方案3】:

    编辑:您最好查看@Benjamin Gruenbaum 的答案,这不仅可以提高性能,还可以提供更简洁的代码。

    承诺 - 是的。

    我很喜欢RSVP,为什么?仅仅因为它是最快的。 (快速基准测试: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);
    });
    

    这是防止代码“永远向右漂移”的方法。

    Promise 很酷,使用它们。

    【讨论】:

    • RSVP 绝对不是最快的。该基准已被破坏,因为它不调用 JIT。 JSPerf 没有任何意义。此外 - 在这个例子中,bluebird 将比 RSVP 快得多,因为您手动承诺分配另一个闭包的函数。您也忽略了错误,但这是另一回事。
    • 基准测试衡量的是使用的调度程序的延迟(您可以在使用 bluebird 时使用 setScheduler 设置),而不是 Promise 的性能。
    • 另外,如果您没有注意到,您的代码比原始回调代码要复杂和冗长得多。如果他只是想删除本来微不足道的代码的“向右漂移”通过命名回调。
    • 那些是非常有效的 cmets - @Benjamin Gruenbaum 的回答在这里特别有启发性。简单到难以置信的情况吧,bluebird 确​​实是更好的选择。更新答案以反映这一点(并捕捉潜在的错误)。
    猜你喜欢
    • 2015-08-01
    • 2017-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多