2016-11-13 25 views
6

我使用的是帶有Node.js的Inquirer庫,在使用Promise時我仍然得到厄運的金字塔,我做錯了什麼?在使用承諾時,我仍然得到厄運的金字塔,我做錯了什麼?

僅供參考問話庫API基本上是:

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

其中的答案是散列,與代表每個問題的密鑰。這裏沒有什麼特別的。

總之,使用API​​,我總是得到getAnswersToPrompts().then(function(answers){}),它似乎更方便地保持嵌套前一個內部的承諾......像這樣:

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

    } 
} 

但這正m讓事情變得更加複雜AFAICT

只要儘可能清楚,我肯定需要先前承諾的結果,但有時我需要承諾之前的結果,甚至是之前的結果。

嵌套功能允許我需要在範圍內的數據,由於閉包等

+1

使用術語「厄運的金字塔」upvote – imjared

+0

大聲笑我真的不介意嵌套的調用,但只是好奇,如果有一種方法 –

+0

你試過'async'模塊與其'async.waterfall' ?你的情況會更清晰。 – Aruna

回答

6

返回下一個承諾之前調用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); 
} 

但有時我需要的從之前的承諾或甚至之前的結果得出的結果

你的例子中唯一的例子是pathsToRun。我認爲爲了適應這種情況,嵌套函數的功能仍然是可讀的,但你的另一種選擇是在承諾鏈之外定義一個變量,我已經在pathsToRun的上面顯示了這個變量。

最後,您的示例在整個承諾鏈中使用了三個不同的變量,所有這些變量都被稱爲answers,這可能會增加混淆。一般來說,我認爲可以使用相同的名稱作爲承諾回調結果,但爲了清晰起見,我在這裏重新命名了這個答案。

+0

不錯,謝謝,當然!恭喜在打這個答案的20K :) –

+0

我添加了一個答案,應該避免對「pathsToRun」等端分配。 –

+0

回覆:「在你的例子中唯一的例子是pathsToRun。」 ...原始代碼中有兩個實例(我沒有更改),「pathsToRun」和「selectedOpts」。下面的答案應該避免函數中的副作用,因爲總是在每個代表累積答案的答案中返回一個新對象,實際上相當整齊。 –

1

@Joe Daley的答案接近完美,但要增加一件事。我真的不喜歡這個函數頂部的變量。我從來不喜歡它與async.waterfall/async.series ...我不喜歡承諾...以下模式應該避免這種情況。我們在每個承諾回調中累積數據,然後在最終的承諾回調中獲得所有數據。

//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 

繁榮!現在,您可以避免對頂部變量的尷尬分配。如果你沒有看到這是如何工作的...問我。

相關問題