Ecmascript 6 你如何实施一个;“竞争成功”;助手,给我一份承诺清单?
我对ES6 Promise API中的某些内容感到困惑。我可以看到并发提交多个异步作业并在第一次成功时“解析”的清晰用例。例如,这将适用于多个等效服务器可用的情况,但有些服务器可能已停机,而另一些服务器负载过重且速度较慢,因此我的目标是从第一个成功的服务器获得响应,而忽略其余的服务器(是的,我知道对于客户端来说,从服务器的角度来看,这是一种令人讨厌的行为方式,但对于最终用户来说,这是一种很好的方式;) 然而,据我所见,我可以使用“全部”或“竞态”行为。“全部”行为似乎要等到所有请求完成,这意味着我必须等待最慢的请求,即使服务器已经完成(事实上,我可能不得不等待超时,在这种情况下,这将是一场灾难。)“竞然而,行为似乎给了我第一个完成的机会,如果这碰巧是一个失败,那也是一场灾难 API中是否有允许“raceToSuccess”行为的东西,或者我必须手工构建它。就此而言,我将如何手工构建它Ecmascript 6 你如何实施一个;“竞争成功”;助手,给我一份承诺清单?,ecmascript-6,es6-promise,Ecmascript 6,Es6 Promise,我对ES6 Promise API中的某些内容感到困惑。我可以看到并发提交多个异步作业并在第一次成功时“解析”的清晰用例。例如,这将适用于多个等效服务器可用的情况,但有些服务器可能已停机,而另一些服务器负载过重且速度较慢,因此我的目标是从第一个成功的服务器获得响应,而忽略其余的服务器(是的,我知道对于客户端来说,从服务器的角度来看,这是一种令人讨厌的行为方式,但对于最终用户来说,这是一种很好的方式;) 然而,据我所见,我可以使用“全部”或“竞态”行为。“全部”行为似乎要等到所有请求完成,这意味着
作为补充说明,我在Java 8 CompletableFuture中发现了同样的难题,它似乎是一个非常并行的API。那么,我是否在哲学层面上遗漏了一些东西?您可以自己轻松编写
function raceToSuccess(promises) {
return new Promise(
resolve =>
promises.forEach(
promise =>
promise.then(resolve)
)
);
}
这将启动所有承诺,当任何承诺成功时,将使用其值解析新承诺。失败的承诺将被忽略。后续成功的承诺不会导致任何事情发生,因为新承诺已被解析。请注意,如果输入承诺均未解析,则生成的承诺将永远不会解析或拒绝
以下是一个修改版本,如果所有输入承诺均被拒绝,则返回被拒绝的承诺:
function raceToSuccess(promises) {
let numRejected = 0;
return new Promise(
(resolve, reject) =>
promises.forEach(
promise =>
promise .
then(resolve) .
catch(
() => {
if (++numRejected === promises.length) reject();
}
)
)
);
}
// ignores any rejects except if all promises rejects
Promise.firstResolve = function (promises) {
return new Promise(function (fulfil, reject) {
var rejectCount = 0;
promises.forEach(function (promise) {
promise.then(fulfil, () => {
rejectCount++;
if(rejectCount == promises.length) {
reject('All promises were rejected');
}
});
});
});
};
我喜欢@loganfsmyth的方法;你可能应该对它的概念清晰性投赞成票。下面是它的一个变体:
function invertPromise(promise) {
return new Promise(
(resolve, reject) =>
promise.then(reject, resolve)
);
}
function raceToSuccess(promises) {
return invertPromise(
Promise.all(
promises.map(invertPromise)));
}
另一个想法是将失败的承诺转化为既不能解决也不能拒绝的承诺(换句话说,是永久未决的),然后使用Promise.race
:
function pendingPromise() { return new Promise(() => { }); }
function killRejected(promise) { return promise.catch(pendingPromise); }
function raceToSuccess(promises) {
return Promise.race(promises.map(killRejected));
}
您可能喜欢也可能不喜欢这种行为。如果输入承诺都没有实现,则返回的承诺将永远不会实现或拒绝。也可能永久挂起的承诺不会得到GC'd,或者某些引擎最终可能会抱怨这些承诺。这是一个经典的示例,在这个示例中,反转逻辑会使其更加清晰。您可以在这种情况下,“种族”是指你希望你的拒绝行为实际上是成功行为
function oneSuccess(promises){
return Promise.all(promises.map(p => {
// If a request fails, count that as a resolution so it will keep
// waiting for other possible successes. If a request succeeds,
// treat it as a rejection so Promise.all immediately bails out.
return p.then(
val => Promise.reject(val),
err => Promise.resolve(err)
);
})).then(
// If '.all' resolved, we've just got an array of errors.
errors => Promise.reject(errors),
// If '.all' rejected, we've got the result we wanted.
val => Promise.resolve(val)
);
}
老话题,但这里是我的条目;它本质上是@loganfsmyth的解决方案,但还需要一些检查,以符合以下方面建立的约定:
- 空数组作为输入(同步)返回已解析的承诺
- 数组中的非承诺项将导致第一个此类项用作解析值
Promise.any=a=>{
返回!长度?
承诺。决心():
我答应你(
e=>(e.then的类型!=='function')?
承诺。拒绝(e):
e、 然后(
结果=>承诺.拒绝(结果),
失败=>Promise.resolve(失败)
)
)).那么(
allRejected=>Promise.Rejected(allRejected),
firstResolved=>Promise.resolve(firstResolved)
);
};
//测试。。。
函数延迟(超时、结果、被拒绝){
返回新承诺((解决、拒绝)=>{
设置超时(
()=>拒绝?拒绝(结果):解决(结果),
超时);
});
}
答应我([
延迟(800,'a'),
延迟(500,'b'),
延迟(250,'c',真)
]).然后(e=>{
log('First resolved(应为b):',e);
});
答应我([
延迟(800,'a',正确),
延迟(500,'b',正确),
延迟(250,'c',真)
])。然后(null,e=>{
log('所有被拒绝(预期失败数组):',e);
});
答应我([
延迟(800,'a'),
延迟(500,'b'),
延迟(250,'c',正确),
“d”,
“e”
]).然后(e=>{
log('First non-promise(预期为d):',e);
});
//因为这是唯一需要同步解决的情况,
//它的输出应该出现在其他输出之前
承诺。任何([])。然后(e=>{
log('空输入(预期未定义):',e);
});
我用超时扩展了@loganfsmyth方法,并编写了一个小函数:
- 履行所有承诺
- 等待承诺成功的时间不超过指定的时间(options.timeOutMs)
- 返回第一个成功的
const firstthattcompletedsuccessfullyes6=(选项)=>{
//回报你的第一个承诺
constOneSuccess=(promises)=>Promise.all(promises.map)(p=>{
//如果请求失败,则将其视为一个解决方案,这样它将保留
//等待其他可能的成功。如果请求成功,
//将其视为拒绝,因此承诺。所有人立即退出。
返回p(
(val)=>{返回承诺。拒绝(val);},
(err)=>{return Promise.resolve(err);}
);
})
).那么(
//如果“.all”被解决,我们就得到了一系列错误。
(错误)=>{返回承诺.拒绝(错误);},
//如果“.all”被拒绝,我们就得到了想要的结果。
(val)=>{return Promise.resolve(val);}
);
//如果先超时,则返回承诺或重新承诺
const timeoutPromise=(毫秒,承诺)=>新承诺(函数(解析,拒绝){
setTimeout(()=>reject(新错误('timeout')),毫秒;
承诺。然后(决定,拒绝);
});
if(options.subsystem.length<1){
return Promise.reject('参数错误,未指定子系统
// fastest promise to end, but is a reject (gets ignored)
var promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("foo")
}, 100);
})
// fastest promise to resolve (wins the race)
var promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("bar")
}, 200);
})
// Another, slower resolve (gets ignored)
var promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("baz")
}, 300);
})
Promise.firstResolve([promise1, promise2, promise3])
.then((res) => {
console.log(res) // "bar"
})
.catch(err => {
console.log(err) // "All promises were rejected" (if all promises were to fail)
})
// ignores any and all rejects
Promise.firstResolve = function (promises) {
return new Promise(function (fulfil) {
promises.forEach(function (promise) {
promise.then(fulfil, () => {});
});
});
};
const PromiseRiceSuccess = <T = unknown>(promises: Promise<T>[]) => {
let done: (reason?: T) => void;
const waitEndAllPromises = new Promise((resolve, reject) => done = reject);
const waitCatchs = promise => Promise.resolve(promise).catch(() => waitEndAllPromises);
Promise.allSettled(promises).then(r => done(r));
return Promise.race(promises.map(waitCatchs));
};
PromiseRiceSuccess([
Promise.reject(1),
new Promise((r) => setTimeout(() => r(2), 4000)),
]);
// 2
PromiseRiceSuccess([
Promise.reject(1),
new Promise((resolve, reject) => setTimeout(() => reject(2), 4000)),
]);
// Uncaught (in promise) (2) [{…}, {…}]
// assume getApi returns a Promise
const promises = [
getApi('url1'),
getApi('url2'),
getApi('url3'),
getApi('url4'),
];
Promise.any(promises)
.then((result) => {
// result will contain the resolve value of the first Promise to resolve
})
.catch((err) => {
// Every Promise rejected
});