使用承诺时我仍然会遇到厄运金字塔,我做错了什么?

I still get the pyramid of doom when using promises, what am I doing wrong?

我将 Inquirer 库与 Node.js 一起使用,但在使用 promises 时我仍然遇到厄运金字塔,我做错了什么?

仅供参考,询问者库 API 基本上是:

inquirer.prompt([
question1,
question2,
question3,
...
questionX
]).then(function(answers){});

其中 answers 是一个散列,其中包含代表每个问题的键。这里没什么特别的。

无论如何,使用 API,我总是得到 getAnswersToPrompts().then(function(answers){}) 并且将 promise 嵌套在前一个中似乎更方便......像这样:

function run (rootDir) {

  return watchHelper().then(function (answers) {

    return chooseDirs({

      allowDirs: answers.allow,
      originalRootDir: rootDir,
      onlyOneFile: false

    }).then(function (pathsToRun) {

      assert(pathsToRun.length > 0, ' You need to select at least one path.');

      return getOptions(availableOptionsForPlainNode).then(function (answers) {

        const selectedOpts = answers[ 'command-line-options' ];

        return localOrGlobal().then(function (answers) {

          const sumanExec = answers.localOrGlobal;

          console.log(' => ', colors.magenta.bold([ '$', sumanExec, '--watch', pathsToRun, selectedOpts ].join(' ')));


        });

      });

    });

  }).catch(rejectionHandler);

}

我可以这样做:

function run(){

  return makePromise()
    .then(fn1(data1))
    .then(fn2(data2))
    .then(fn3(data3))

}

其中 fn1、fn2、fn3 看起来像:

function fnX(data){

   return function(answers){

      return promise(data);

   }
}

但这只会让理解 AFAICT 变得更加复杂

尽量说清楚,我肯定需要之前承诺的结果,但有时我需要之前承诺的结果,甚至需要之前的结果。

由于闭包等,嵌套函数允许我需要的数据在范围内。

Return 下一个 Promise before 调用 then:

function run (rootDir) {
  var pathsToRun;

  return watchHelper()
    .then(function (watchHelperAnswers) {
      return chooseDirs({
        allowDirs: watchHelperAnswers.allow,
        originalRootDir: rootDir,
        onlyOneFile: false
      });
    }).then(function (chooseDirsResult) {
      assert(chooseDirsResult.length > 0, ' You need to select at least one path.');
      pathsToRun = chooseDirsResult;
      return getOptions(availableOptionsForPlainNode);
    }).then(function (getOptionsAnswers) {
      const selectedOpts = getOptionsAnswers[ 'command-line-options' ];
      return localOrGlobal();
    }).then(function (localOrGlobalAnswers) {
      const sumanExec = localOrGlobalAnswers.localOrGlobal;
      console.log(' => ', colors.magenta.bold([ '$', sumanExec, '--watch', pathsToRun,
        selectedOpts ].join(' ')));
    }).catch(rejectionHandler);
}

but sometimes I need the result from the promise prior to that or even the result prior to that

您的示例中的唯一实例是 pathsToRun。我认为嵌套两个或三个深度的函数以容纳它仍然是可读的,但你的另一个选择是在承诺链之外定义一个变量,我在上面为 pathsToRun.

显示了这一点

最后,您的示例在整个承诺链中使用了三个不同的变量,它们都称为 answers,这可能会增加混乱。总的来说,我认为对 promise 回调结果使用相同的名称是可以的,但为了清楚起见,我在这里重命名了它们。

@Joe Daley 的回答近乎完美,但还要补充一件事。我真的不喜欢在函数顶部对变量进行边赋值。我从不喜欢 async.waterfall/async.series...我也不喜欢 promises...下面的模式应该避免这种情况。我们在每个 promise 回调中累积数据,然后在最终的 promise 回调中我们拥有所有数据。

//start 

    function run (rootDir) {

      return watchHelper().then(function (watchHelperAnswers) {

          return chooseDirs({
            allowDirs: watchHelperAnswers.allow,
            originalRootDir: rootDir,
            onlyOneFile: false
          });

        }).then(function (chooseDirsResult) {

         return getOptions(availableOptions).then(function(options){
              return {       //accumulate answers
                 options: options,
                 pathsToRun: chooseDirsResult
               }
         });

        }).then(function (obj) {

          return localOrGlobal().then(function(answers){
                return Object.assign(obj,{   //accumulate answers
                     localOrGlobal: answers.localOrGlobal
                  });
          });

        }).then(function (obj) {

          const {...allTheAnswers} = obj;

        }).catch(rejectionHandler);
    }

//end

轰!现在您可以避免在顶部对变量进行笨拙的赋值。如果你不明白这是如何工作的...问我。