Javascript 对nodejs中的q和承诺有点困惑

Javascript 对nodejs中的q和承诺有点困惑,javascript,node.js,promise,q,Javascript,Node.js,Promise,Q,我目前在nodejs中有一些js文件,它们作为模块加载并扩展app对象(使用express) 所以他们的签名看起来像: module.exports = function(app, callback) { // ... callback(); } require("./setup/a")(app, function() { require("./setup/b")(app, function(){ require("./setup/c")(app, fu

我目前在nodejs中有一些js文件,它们作为模块加载并扩展app对象(使用express)

所以他们的签名看起来像:

module.exports = function(app, callback) { 
   // ... 
   callback();
}
require("./setup/a")(app, function() {
    require("./setup/b")(app, function(){
        require("./setup/c")(app, function(){
            require("./setup/d")(app, function(){
                require("./setup/e")(app, function(){
                    startApp();
                })
            })
        })
    })
});
因此,目前我有大约5个,我的代码如下所示:

module.exports = function(app, callback) { 
   // ... 
   callback();
}
require("./setup/a")(app, function() {
    require("./setup/b")(app, function(){
        require("./setup/c")(app, function(){
            require("./setup/d")(app, function(){
                require("./setup/e")(app, function(){
                    startApp();
                })
            })
        })
    })
});
现在这看起来很难看,因为它是“末日金字塔”,但是我不完全确定我需要如何改变这个模式来使用Q,因为我假设我会使用
Q.fcall(…a).then(…b).etc.done()
。但是,我不确定如何将应用程序传递给它,以及是否需要返回回调以使其作为承诺进行处理


理想情况下,我不想在我的代码中开始重击Q,我只希望它出现在我想要删除金字塔用例的地方,那么在上面的示例中,我如何使用带有承诺的Q将应用程序传递到每个必需的模块,然后在最后启动应用程序?

假设您的模块尚未使用承诺,您可以执行以下操作:

module.exports = function(app) { 
    // do some stuff with app
    return new Promise(function(resolve,reject){
        // when ready to resolve after some actions on app
        resolve(); // you can also return a value here as a cb param
    });
};

Promise.all(["./setup/a","./setup/b","./setup/c"].map(require.bind(null,app)))
  .then(startApp);
但是,您应该尽可能使用最低级别的承诺,这意味着您可以简单地返回您在流程中使用的承诺:

module.exports = function(app){
     return something(app).then(function(){
         return somethingElseAsyncWithApp(app);
     });
};
因此,不需要承诺构造函数。请注意,这个答案使用本机承诺,但也适用于使用该语法的库,如Bluebird。对于Q更改
new Promise
new Q.Promise
Promise.all
Q.all


或者,您可以将每个
require(x)
更改为
Q.fcall(require,x)
,并直接对其使用
Q.all
,但这既慢(尽管Q很慢),也比直接提示模块更容易出错。最好是尽可能提供最低级别的API。

承诺不是挽救末日金字塔的灵丹妙药。我见过这样的代码,即使有承诺,它看起来也像是一个末日金字塔

您可以通过以下操作摆脱金字塔并保持回调样式:

  // var app;
  // ...etc
  var paths = ['a','b','c','d'];
  setupNext();

  function setupNext() {
    var p = paths.pop(); // or shift
    var next = paths.length > 0 ? setupNext : startApp
    require(p)(app, next);
  }

  function startApp() {}

感谢您的示例,所以
Promise
是Node中可用的默认类的一部分吗?我认为它是特定于Q或其他Promise库的。在构建脚本中,我在其他地方使用async进行并行和串行调用,但正如我在代码中的一些地方所知道的那样,我希望避免使用金字塔和
x.then(y).then(z)
看起来更具吸引力,因为它更具可读性。同样在您的第一个示例中,应用程序是如何传递到
Promise的。all
call?
async
在这里也可以工作,但是promises是一个更强大的抽象<代码>承诺。所有聚合承诺,应用程序被传递到每个require调用中-我忘记了,我会修复它。至于
承诺
-它取决于节点的版本。就我个人而言,我会使用蓝鸟,它的堆栈轨迹比Q好得多,速度快两个数量级。好的advive,会检查它的。我以前从来都不需要承诺,所以我也没有仔细研究过,但是当我遇到金字塔型的情况时,做一些挖掘是有意义的。