实现 Promise.all 和 Promise.settle 的混合
Implementing a mix of Promise.all and Promise.settle
我需要实现 Promise.all
的一个版本,它将采用一系列承诺和 return 结果,就像它通常所做的那样,另外还解决所有承诺,就像 Promise.settle
在 Bluebird
库中执行它,除了我不能使用 Bluebird
,并且必须仅依赖于标准承诺协议。
实施起来会不会非常复杂?还是在这里询问如何实施它的想法太多了?我真的希望不会,所以我想问一下,如果有人以前实施过它,请分享如何正确实施的想法。
这样做的前提是能够在调用完成后需要执行 commit
/rollback
的数据库事务中使用它,并且它不能有松散的承诺仍在尝试在事务调用之外解决。
编辑: 提供给另一个问题的 link 非常有用,但它不是所提问题的完整答案。一个通用的 settle
是一个很好的例子,它有很大帮助,但它需要被简化并包装到 all
逻辑中以适应前面描述的事务场景。
经过所有的研究、编写、测试和优化,它变成了一个专注于此类事物的库(spex)。
具体来说,方法 batch 是实现所述逻辑融合的方法。
我不会重新发布its source code here, because it now does a lot more问题中最初寻求的内容。
基于 中的通用 promiseSettle()
函数,您可以执行此操作并同时拥有通用 settle()
类型函数和更具体的版本作为它的包装器。这会给你通用的能力来做很多 .settle()
类型的行为,并有你自己的特定风格,还可以根据需要构建其他特定风格:
因此,这是通用的 promiseSettle()
,returns 您可以了解所有承诺的状态,并且仅在所有传入的承诺完成后才解析:
function promiseSettle(promises) {
return new Promise(function(resolve) {
var remaining = promises.length;
// place to store results in original order
var results = new Array(remaining);
function checkDone() {
if (--remaining === 0) {
resolve(results);
}
}
promises.forEach(function(item, index) {
// check if the array entry is actually a thenable
if (typeof item.then === "function") {
item.then(function(value) {
// success
results[index] = {state: "fulfilled", value: value};
checkDone();
}, function(err) {
// reject error
results[index] = {state: "rejected", value: err};
checkDone();
});
} else {
// not a thenable, just return the item itself
results[index] = {state: "fulfilled", value: item}
--remaining;
}
});
// special case for zero promises passed
if (remaining === 0) {
resolve(results);
}
});
}
而且,这里有一个包装器可以为您提供特定的行为:
// Either fulfills with an array of results or
// rejects with the first error, but it does not do either
// until all promises have completed which makes it different than
// promise.all()
function promiseSettleAll(promises) {
return promiseSettle(promises).then(function(results) {
for (var i = 0; i < results.length; i++) {
if (results[i].state !== "fulfilled") {
// reject with the first error found
throw results[i].value;
}
}
// all were successful, return just array of values
return results.map(function(item) {return item.value;});
});
}
我认为 jfriend 的解决方案过于复杂,因为它建立在 settle
之上,它运行一个信号量并做很多奇怪的事情,而不是使用像 .all
这样的内置原语.
相反,如果我们基于 Bluebird 的更新 reflect
原语(在本机承诺中实现它),我们可以获得更清晰的 API 和实现:
function reflect(promise){
return promise.then(x => ({state: "fulfilled", value: x}), // arrows, assume nodejs
e => ({state: "rejected" , value: e}));
}
在 reflect 之上,我们可以轻松构建其他原语:
function settle(promises){
return Promise.all(promises.map(reflect)); // much cleaner
}
如果我们想等待然后 resolve/reject 基于值,它很简单:
function allWait(promises){
return settle(promises).then(results => {
var firstFailed = results.find(r => r.state === "rejected");
if(firstFailed) throw firstFailed.value;
return results;
});
}
我需要实现 Promise.all
的一个版本,它将采用一系列承诺和 return 结果,就像它通常所做的那样,另外还解决所有承诺,就像 Promise.settle
在 Bluebird
库中执行它,除了我不能使用 Bluebird
,并且必须仅依赖于标准承诺协议。
实施起来会不会非常复杂?还是在这里询问如何实施它的想法太多了?我真的希望不会,所以我想问一下,如果有人以前实施过它,请分享如何正确实施的想法。
这样做的前提是能够在调用完成后需要执行 commit
/rollback
的数据库事务中使用它,并且它不能有松散的承诺仍在尝试在事务调用之外解决。
编辑: 提供给另一个问题的 link 非常有用,但它不是所提问题的完整答案。一个通用的 settle
是一个很好的例子,它有很大帮助,但它需要被简化并包装到 all
逻辑中以适应前面描述的事务场景。
经过所有的研究、编写、测试和优化,它变成了一个专注于此类事物的库(spex)。
具体来说,方法 batch 是实现所述逻辑融合的方法。
我不会重新发布its source code here, because it now does a lot more问题中最初寻求的内容。
基于 promiseSettle()
函数,您可以执行此操作并同时拥有通用 settle()
类型函数和更具体的版本作为它的包装器。这会给你通用的能力来做很多 .settle()
类型的行为,并有你自己的特定风格,还可以根据需要构建其他特定风格:
因此,这是通用的 promiseSettle()
,returns 您可以了解所有承诺的状态,并且仅在所有传入的承诺完成后才解析:
function promiseSettle(promises) {
return new Promise(function(resolve) {
var remaining = promises.length;
// place to store results in original order
var results = new Array(remaining);
function checkDone() {
if (--remaining === 0) {
resolve(results);
}
}
promises.forEach(function(item, index) {
// check if the array entry is actually a thenable
if (typeof item.then === "function") {
item.then(function(value) {
// success
results[index] = {state: "fulfilled", value: value};
checkDone();
}, function(err) {
// reject error
results[index] = {state: "rejected", value: err};
checkDone();
});
} else {
// not a thenable, just return the item itself
results[index] = {state: "fulfilled", value: item}
--remaining;
}
});
// special case for zero promises passed
if (remaining === 0) {
resolve(results);
}
});
}
而且,这里有一个包装器可以为您提供特定的行为:
// Either fulfills with an array of results or
// rejects with the first error, but it does not do either
// until all promises have completed which makes it different than
// promise.all()
function promiseSettleAll(promises) {
return promiseSettle(promises).then(function(results) {
for (var i = 0; i < results.length; i++) {
if (results[i].state !== "fulfilled") {
// reject with the first error found
throw results[i].value;
}
}
// all were successful, return just array of values
return results.map(function(item) {return item.value;});
});
}
我认为 jfriend 的解决方案过于复杂,因为它建立在 settle
之上,它运行一个信号量并做很多奇怪的事情,而不是使用像 .all
这样的内置原语.
相反,如果我们基于 Bluebird 的更新 reflect
原语(在本机承诺中实现它),我们可以获得更清晰的 API 和实现:
function reflect(promise){
return promise.then(x => ({state: "fulfilled", value: x}), // arrows, assume nodejs
e => ({state: "rejected" , value: e}));
}
在 reflect 之上,我们可以轻松构建其他原语:
function settle(promises){
return Promise.all(promises.map(reflect)); // much cleaner
}
如果我们想等待然后 resolve/reject 基于值,它很简单:
function allWait(promises){
return settle(promises).then(results => {
var firstFailed = results.find(r => r.state === "rejected");
if(firstFailed) throw firstFailed.value;
return results;
});
}