使用承诺时我仍然会遇到厄运金字塔,我做错了什么?
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
轰!现在您可以避免在顶部对变量进行笨拙的赋值。如果你不明白这是如何工作的...问我。
我将 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
轰!现在您可以避免在顶部对变量进行笨拙的赋值。如果你不明白这是如何工作的...问我。