pipe理承诺依赖关系

我正在使用Node.js和Bluebird创build一些相当复杂的逻辑,包括解压缩结构化文件,parsingJSON,创build和更改多个MongoDB文档,以及在多个位置写入相关文件。 我也有相当复杂的error handling,这取决于发生错误时系统的状态。

我很难想出一个通过承诺来pipe理依赖关系的好方法。

我现有的代码基本上是这样的:

var doStuff = function () { var dependency1 = null; var dependency2 = null; promise1() .then(function (value) { dependency1 = value; return promise2() .then(function (value) { dependency2 = value; return promise3(dependency1) .then(successFunction); }); }) .catch(function (err) { cleanupDependingOnSystemState(err, dependency1, dependency2); }); }; 

请注意,在promise3之前不需要dependency1,并且error handling程序需要知道依赖关系。

对我来说,这似乎是意大利面代码(和我的实际代码是很多并行控制stream程更糟糕)。 我也读过,在.thencallback中返回另一个promise是一个反模式。 有没有更好的/更干净的方式来完成我想要做的事情?

我发现两个答案目前提供的很好,但笨拙。 他们都很好,但包含开销,我不认为你需要。 如果你使用承诺作为代理,你可以免费获得很多东西。

 var doStuff = function () { var p1 = promise1(); var p2 = p1.then(promise2); var p3 = p1.then(promise3); // if you actually need to wait for p2 here, do. return Promise.all([p1, p2, p3]).catch(function(err){ // clean up based on err and state, can unwrap promises here }); }; 

请不要使用successFunction ,这是一个反模式,并丢失信息。 如果你觉得你必须使用successFunction你可以写:

 var doStuff = function () { var p1 = promise1(); var p2 = p1.then(promise2); var p3 = p1.then(promise3); // if you actually need to wait for p2 here, do. Promise.join(p1, p2, p3, successFunction).catch(function(err){ // clean up based on err and state, can unwrap promises here }); }; 

然而,这是无穷的,因为它不会让消费者处理他们可能会遇到的错误。

这个问题可能更适合于code review但这里是我如何处理它给这个例子:

 var doStuff = function () { // Set up your promises based on their dependencies. In your example // promise2 does not use dependency1 so I left them unrelated. var dep1Promise = promise1(); var dep2Promise = promise2(); var dep3Promise = dependency1Promise.then(function(value){ return promise3(value); }); // Wait for all the promises the either succeed or error. allResolved([dep1Promise, dep2Promise, dep3Promise]) .spread(function(dep1, dep2, dep3){ var err = dep1.error || dep2.error || dep3.error; if (err){ // If any errored, call the function you prescribed cleanupDependingOnSystemState(err, dep1.value, dep2.value); } else { // Call the success handler. successFunction(dep3.value); } }; // Promise.all by default just fails on the first error, but since // you want to pass any partial results to cleanupDependingOnSystemState, // I added this helper. function allResolved(promises){ return Promise.all(promises.map(function(promise){ return promise.then(function(value){ return {value: value}; }, function(err){ return {error: err}; }); }); } 

allResolved的使用只是因为你的callback细节,如果你有一个更一般的error handling程序,你可以直接使用Promise.all直接parsing,或者甚至:

 var doStuff = function () { // Set up your promises based on their dependencies. In your example // promise2 does not use dependency1 so I left them unrelated. var dep1Promise = promise1(); var dep2Promise = promise2(); var dep3Promise = dependency1Promise.then(function(value){ return promise3(value); }); dep3Promise.then(successFunction, cleanupDependingOnSystemState); }; 

这当然不是一个反对模式,承诺是在诺言规范的一个特点。

这里有一个可能的重写,但我不确定它是否更清晰:

 var doStuff = function () { promise1() .then(function (value1) { return promise2() .then(function (value2) { return promise3(value1) .then(successFunction) .finally(function() { cleanup(null, value1, value2); }); }) .finally(function() { cleanup(null, value1, null); }); }) .finally(function () { cleanup(null, null, null); }); }; 

或另一个选项,具有primefaces清理function:

 var doStuff = function () { promise1() .then(function (value1) { return promise2() .then(function (value2) { return promise3(value1) .then(successFunction) .finally(function() { cleanup3(value2); }); }) .finally(function() { cleanup2(value1); }); }) .finally(function (err) { cleanup1(err); }); }; 

真的,我觉得你没办法清理这些东西。 与香草try/catch事件,最好的模式是非常相似的这些。