管理承诺依赖
Managing promise dependencies
我正在使用 Node.js 和 Bluebird 创建一些相当复杂的逻辑,包括解压缩结构化文件、解析 JSON、创建和更改多个 MongoDB 文档,以及编写多个位置的相关文件。根据发生错误时系统的状态,我还对所有这些进行了相当复杂的错误处理。
我很难想出一个通过承诺流管理依赖关系的好方法。
我现有的代码基本上是这样的:
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,并且错误处理程序需要了解依赖项。
对我来说,这似乎是意大利面条代码(而且我的实际代码更糟糕,因为有很多并行控制流)。我还读到在 .then 回调中返回另一个 promise 是一种反模式。有没有 better/cleaner 方法来完成我想做的事情?
这个问题可能更适合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
只是因为你的回调细节,如果你有一个更通用的错误处理程序,你可以直接使用 Promise.all
解决,甚至:
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);
};
在 then
秒内肯定不是 return promises 的反模式,展平嵌套 promises 是 promise 规范的一个特性。
这是一个可能的重写,但我不确定它是否更清晰:
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);
});
};
或另一种选择,具有原子清理功能:
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
es,最佳模式与这些非常相似。
我发现目前提供的两个答案都很好但很笨拙。它们都很好但包含开销我认为你不需要。如果您改为使用 promises 作为代理,您可以免费获得很多东西。
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
});
};
然而,它更糟糕,因为它不会让消费者处理他们可能能够处理的错误。
我正在使用 Node.js 和 Bluebird 创建一些相当复杂的逻辑,包括解压缩结构化文件、解析 JSON、创建和更改多个 MongoDB 文档,以及编写多个位置的相关文件。根据发生错误时系统的状态,我还对所有这些进行了相当复杂的错误处理。
我很难想出一个通过承诺流管理依赖关系的好方法。
我现有的代码基本上是这样的:
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,并且错误处理程序需要了解依赖项。
对我来说,这似乎是意大利面条代码(而且我的实际代码更糟糕,因为有很多并行控制流)。我还读到在 .then 回调中返回另一个 promise 是一种反模式。有没有 better/cleaner 方法来完成我想做的事情?
这个问题可能更适合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
只是因为你的回调细节,如果你有一个更通用的错误处理程序,你可以直接使用 Promise.all
解决,甚至:
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);
};
在 then
秒内肯定不是 return promises 的反模式,展平嵌套 promises 是 promise 规范的一个特性。
这是一个可能的重写,但我不确定它是否更清晰:
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);
});
};
或另一种选择,具有原子清理功能:
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
es,最佳模式与这些非常相似。
我发现目前提供的两个答案都很好但很笨拙。它们都很好但包含开销我认为你不需要。如果您改为使用 promises 作为代理,您可以免费获得很多东西。
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
});
};
然而,它更糟糕,因为它不会让消费者处理他们可能能够处理的错误。