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

时间:2016-11-13 02:09:31

标签: javascript node.js es6-promise

我正在使用带有Node.js的Inquirer库,在使用promises时我仍然得到了厄运的金字塔,我做错了什么?

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

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

其中答案是哈希值,其中键代表每个问题。这里没什么不寻常的。

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

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的事情变得更加复杂

尽可能清楚,我肯定需要先前承诺的结果,但有时我需要之前的承诺结果,甚至是之前的结果。

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

2 个答案:

答案 0 :(得分:6)

在调用then之前返回下一个Promise

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

但有时我需要之前的承诺结果,甚至是

之前的结果

您示例中唯一的例子是pathsToRun。我认为嵌套函数有两到三个深度来容纳这个仍然是可读的,但你的另一个选择是在promise链之外定义一个变量,我在上面为pathsToRun显示了这个变量。

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

答案 1 :(得分:1)

@Joe Daley的答案接近完美,但还要补充一点。我真的不喜欢在函数顶部对变量进行侧面赋值。我从来都不喜欢async.waterfall / async.series ......我也不喜欢承诺...以下模式应该避免这种情况。我们在每个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

热潮!现在,您可以避免在顶部对变量进行笨拙的分配。如果你看不出它是如何工作的......请问我。