Javascript 在承诺返回函数中找到第一个成功
给定一些函数,返回承诺:Javascript 在承诺返回函数中找到第一个成功,javascript,node.js,asynchronous,promise,short-circuiting,Javascript,Node.js,Asynchronous,Promise,Short Circuiting,给定一些函数,返回承诺: function foo(arg) { return new Promise(function(resolve, reject) { if (stuff(arg)) { resolve('result from foo'); } else { resolve(null); } }); ); // ... maybe more of these functions ... function bar(arg) {
function foo(arg) {
return new Promise(function(resolve, reject) {
if (stuff(arg)) {
resolve('result from foo');
} else {
resolve(null);
}
});
);
// ... maybe more of these functions ...
function bar(arg) {
return new Promise(function(resolve, reject) {
if (otherStuff(arg)) {
resolve('result from bar');
} else {
resolve(null);
}
});
);
我们如何以串行方式迭代函数,在第一个函数返回非空值后短路?
[
foo,
// ...
bar
].firstWithArg('some arg')
.then(function(result) {
// result: 'result from ___', or `null`
});
基本上,期望的行为是:
new Promise(function(resolve, reject){
foo('some-arg')
.then(function(result) {
if (result) {
resolve(result);
} else {
// ...
bar('some-arg')
.then(function(result) {
if (result) {
resolve(result);
} else {
resolve(null); // no functions left
}
})
}
});
});
Promise.race()不能使用,因为函数不能全部启动。它们必须连续执行,在第一次成功后停止。我不认为有任何预先构建的内容。您可以创建自己的,无需太多工作。假设您有一个函数数组,在调用时返回承诺。然后,您可以迭代该数组,并在得到喜欢的结果时停止。当序列中的承诺被拒绝时,不清楚您想要做什么-此实现继续到下一个函数,但您可以为该情况编写任何您想要的行为:
function iterateUntilGood(list, args) {
var cntr = 0;
return new Promise(function(resolve, reject) {
function next() {
if (list.length > cntr) {
list[cntr++].apply(null, args).then(function(result) {
// check the result here
if (some condition) {
resolve(result);
} else {
next();
}
}, next);
} else {
reject("No function succeeded");
}
}
next();
});
}
// usage
iterateUntilGood([fn1, fn2, fn3, fn4], [arg1, arg2]).then(function(result) {
// got result here
}, function(err) {
// handle error here
});
工作演示:您说过您的第一个问题实际上只是第二个问题的设置,这才是真正的问题 所以我想你的问题是:如何执行一系列函数,当第一个函数解析为非null值时,它们会串行地返回承诺,短路 我可能不会,我会使用
拒绝
而不是解析(null)
(但在一篇评论中,您已经澄清了您想要解析(null)
,我明白您的观点;我将在下面介绍这一点):
function-otherStuff(arg){
返回arg==2;
}
函数填充(arg){
返回arg==“c”;
}
函数foo(arg){
log(“foo:”,arg);
返回新承诺(功能(解决、拒绝){
if(填充(arg)){
log(“foo:,arg,“解析”);
解决(“来自foo的结果”);
}否则{
log(“foo:,arg,“拒绝”);
拒绝();//foo(“b”))
.catch(()=>bar(2))
.catch(()=>foo(“c”))
.catch(()=>bar(3))
.然后(值=>{
console.log(“完成”,值);
});
多亏了@T.J.Crowder和@jfriend00的回答
TL;DR:
目标:迭代返回承诺的函数,直到我们成功地用一个值
解析
,然后我们短路。我们不想承诺。竞赛
,而是串行运行函数
有关完整的工作示例,请参见此代码段:
/*定义返回承诺履行以下合同的函数:
*开关(状态){
*成功案例:
*决心(结果);
*中断;
*案例失败:
*解析(空);
*中断;
*案例错误:
*拒绝(错误);
* }
*/
常量函数=[
arg=>新承诺((解决)=>{
console.log('对照检查',arg);
如果(arg=='a'){
决议(“A”);
}否则{
解决();
}
}),
arg=>新承诺((解决)=>{
log('对照'arg'检查b');
如果(arg=='b'){
决议(“B”);
}否则{
解决();
}
}),
//故意省略处理“c”
arg=>新承诺((解决、拒绝)=>{
console.log('对照检查数据',arg);
如果(arg=='d'){
log(“模拟错误”);
拒绝(新错误('D'));
}否则{
解决();
}
}),
arg=>新承诺((解决)=>{
console.log('对照检查e',arg);
如果(arg=='e'){
决议(‘E’);
}否则{
解决();
}
})
];
/*连续调用具有给定参数的函数,直到解析出一个值,
*然后我们短路。
*/
函数委托(arg){
console.log('\n为''arg'删除);
减少(
//请注意,此空比较总是发生N次,
//其中N是函数数
//(除非其中一个函数拒绝)
(p,fn)=>p.then(r=>r?r:fn(arg)),
Promise.resolve(null)
).然后(值=>{
log('Done:',值);
})
.catch(错误=>{
console.log('Error:',Error);
});
}
//通过委托函数运行示例输入
[a',b',c',d',e']{
setTimeout(delegate.bind(null,e),i*100);//延迟,用于打印
});
每个问题问一个问题。你上面的问题有完全不同的用例和解决方案,你的第一个问题与你的问题标题无关。@T.J.Crowder我真的把第一个问题作为第二个问题的类比。@jfriend00我看了Promise.race(),但它不适合我的用例。我需要串行执行函数,并且短路,而不是全部启动,然后得到第一个完成的结果。@bosticko:上面还不清楚。我建议澄清一下。如果有大量函数,有没有一种方法可以通过编程方式链接调用?@bosticko-是的,在我的答案,你似乎没有注意到。@bosticko:你是说如果你有一个函数数组要调用吗?@t.J.Crowder,确切地说,是一个函数数组。@bosticko:是的。甚至还有一个习惯用法。我已经把它添加到了答案中。所以你在函数列表中递归迭代,直到找到想要的结果为止?@bosticko-是的,基本上是这样。这不是常规递归,因为它们是异步操作,所以没有堆栈构建。但是,它使用一个公共函数来执行迭代的下一步,直到找到喜欢的结果并希望停止迭代为止。这是序列化异步操作的一个公共模式。您可以执行下一个异步操作在完成之前的一个过程中进行操作。@bosticko-我在我的解决方案中添加了数组中函数的参数。如果您有任何反馈,我很高兴在代码片段中看到这一点,但我相信您。:-@T.J.Crowder-我添加了一个jsFiddle.@T.J.Crowder我已经将您的答案改编为我正在寻找的内容。谢谢您的帮助。
function foo(arg) {
return new Promise(function(resolve, reject) {
if (stuff(arg)) {
resolve('result from foo');
} else {
reject(); // <=== Note
}
});
}
// ... maybe more of these functions ...
function bar(arg) {
return new Promise(function(resolve, reject) {
if (otherStuff(arg)) {
resolve('result from bar');
} else {
reject(); // <=== Note
}
});
}
foo("a")
.catch(() => bar(1))
.catch(() => foo("b"))
.catch(() => bar(2))
.catch(() => foo("c"))
.catch(() => bar(3))
.then(value => {
console.log("Done", value);
});
const arg = 'some common arg';
const functions = [
arg => new Promise((resolve, reject) => {
/* Does some work, then calls:
* resolve(something) if success
* resolve(null) if failure
* reject(error) if error
*/
})
]
functions.reduce(
(prev, fn) => prev.then(res => res ? res : fn(arg)),
Promise.resolve(null) // base case
) // returns promise which honours same contract as functions
// (resolves with something or null, or rejects with error)