【问题标题】:How to transform a callback hell into promise?如何将回调地狱转化为承诺?
【发布时间】:2014-06-21 01:58:46
【问题描述】:

考虑到这个例子:

app.get('/url/:param', function(req, res, next) {
  var param = req.params.param;
  method1(param, function(err, model) {
    if(err) return next(err);
    method2(model, function(err, models) {
      if(err) return next(err);
      if(models.length > 0) {
        method3(mode, models, function(err, models2) {
          res.render("index.html", {model: model, models: models, models2: models2});
        });
      } else {
        res.render("index.html", {model: model, models: models});
      }
    });
  });
}

有没有办法使用 Promise 来简化这个例子? 看看所有的方法都是依赖的并且有像if(models.length > 0)这样的验证 此外,我需要将res.render 分组到一个独特的位置。

【问题讨论】:

    标签: javascript express callback promise bluebird


    【解决方案1】:

    假设 Bluebird,利用库功能并移除嵌套:

    var api = Promise.promisifyAll({method1:method1,method2:method2:method3:method3});
    api.method1Async(req.params.param).then(function(res){
         return [method2Async(res),{model:res}];
    }).spread(function(res,result){
         result.models = res;
         if(models.length > 0){
             return method3Async(mode,res).then(function(res){
                 result.models2 = res;
             }).return(result);
         }
         return results;
    }).then(function(results){
         res.render("index.html",results);
    }).catch(next); // also handle render errors
    

    或者,你可以做一些嵌套更少的事情:

    var api = Promise.promisifyAll({method1:method1,method2:method2:method3:method3});
    var p1 = api.method1Async(req.params.param);
    var p2 = p1.then(api.method2Async);
    var p3 = p2.then(function(models){ 
        if(models.length > 0) return api.method3Async(mode,models);
    });
    Promise.all([p1,p2,p3]).spread(function(r1,r2,r3){
        var results = {model:r1,models:r2};
        if(models.length > 0) results.models2 = r3;
        res.render("index.html",results);
    }).catch(next)
    

    或者,您可以使用 Bluebird 协程(需要 --harmony-generators 标志)

    var api = Promise.promisifyAll({method1:method1,method2:method2:method3:method3});
    Promise.coroutine(function*(){
        var model = yield method1Async(req.params.param);
        var models = yield method2Async(model);
        if(models.length > 0){
            var models2 = yield method3Async(mode,models);     
        }
        res.render("index.html",{model:model,models:models,models2:models2});
    })().catch(next); // should probably extract to a clear method rather than self invoke
    

    【讨论】:

    • +1 对于第二个示例-尽管我要补充一点,在某些情况下,可以直接使用 res.render("index.html", {model:r1,models:r2,models2:r3}); (如果视图中没有“对象中存在键”检查)
    • 我什至在协程版本中采用了该捷径,但从技术上讲 - 好点。
    • 哇,第三个例子真的很酷,因为它就像同步编程,但我是一个 javascript 初学者,可能不像看起来那么简单。第二个示例似乎是一个好方法,但您需要重复验证。
    • 您可以将缺少检查的选项从第三个选项混合到第二个选项,并保存双重检查。我不想假设你可以通过 undefined。
    【解决方案2】:

    是的,有。您可以将其减少为一个失败或成功的承诺,并安装nextres.render 作为各自的处理程序。然而,由于结果依赖于所有先前的结果,金字塔仍然是最简单的。假设Q.js

    app.get('/url/:param', function(req, res, next) {
      Q.nfcall(method1, req.params.param).then(function(model) {
        return Q.nfcall(method2, model).then(function(models) {
          if(models.length > 0)
            return Q.nfcall(method3, mode, models).catch(function(err) {
              // you're not dealing with errors here
              // omit this `.catch()` call if you want to let it reach the `next()`
            }).then(function(models2) {
              return {model: model, models: models, models2: models2};
            });
          else
            return {model: model, models: models};
          }
        });
      }).done(function(results) {
        res.render("index.html", results);
      }, next);
    }
    

    【讨论】:

    • 虽然我通常是 Promise 的粉丝,但目前还不清楚这是否比 OP 开始时更简单。我确信它有一些优点,但简单并不是其中之一。我也想知道 OP 在他们的版本中的错误检查。
    • @jfriend00 他在最后的.done 处理程序中调用next。它具有与 OP 相同的错误处理,只是不那么冗长。
    • OP 忘记了一个if (err) 检查,这足以导致极难发现的错误(相当于on error resume next)。所以就不一样了。
    • @Esailija:你说得对,我已经调整了解决方案。
    【解决方案3】:

    如果我说得对,如果你需要做很多嵌套语句,也许你可以检查this。 Async 可以帮助您调用行上的各种函数(有很多方法可以使用 Async 来完成它,请参阅其文档进一步)并且您可以将所有语句放在一起。

    【讨论】:

    • Async 就像是 Bluebird Promise 的一个速度较慢、功能较差且笨重的版本,没有我们从 Bluebird 和 Promise 中获得的良好语法和抛出安全性等所有好处。
    猜你喜欢
    • 2016-08-30
    • 2019-09-18
    • 1970-01-01
    • 2020-12-24
    • 1970-01-01
    • 2017-02-09
    • 1970-01-01
    • 2018-05-04
    • 2020-06-02
    相关资源
    最近更新 更多