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

I use the Inquirer library with Node.js and I still get the doom pyramid when using promises, what am I doing wrong?

Just FYI request library API basically:

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

where the answers are a hash, with keys that represent each question. There is nothing unusual here.

In any case, using the API, I always get getAnswersToPrompts().then(function(answers){}) , and it seems more convenient to keep the nested promises inside the previous one ... like this:

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

I could do this instead:

 function run(){ return makePromise() .then(fn1(data1)) .then(fn2(data2)) .then(fn3(data3)) } 

where fn1, fn2, fn3 are as follows:

 function fnX(data){ return function(answers){ return promise(data); } } 

but it only complicates the understanding of AFAICT

Just be extremely clear, I definitely need the result of the previous promise, but sometimes I need the result from the promise to this or even the result to that.

Nesting functions allows me to get the data I need using closures, etc.

+6
source share
2 answers

Return the following promise before calling 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 a result from a promise to this or even a result to that

The only example of this in your example is pathsToRun . I think that two or three deep nesting functions, to fit this, are still readable, but another option is to define a variable outside the promise chain, which I showed above for pathsToRun .

Finally, your example uses three different variables, all called answers , throughout the promise chain, which can add to the confusion. All in all, I think it's good to use the same name to promise callback results, but I renamed them here for clarity in this answer.

+6
source

@Joe Daley answers almost perfectly, but add one more thing. I really don't like assigning this side to variables at the top of the function. I never liked this with async.waterfall / async.series ... and I don't like it with promises either ... the following template should avoid this. We accumulate data in each promise callback, and then in the last callback we promise that we have all the data.

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

bang! Now you can avoid this awkward assignment of variables at the top. If you do not see how this works ... ask me.

+1
source

Source: https://habr.com/ru/post/1012269/


All Articles