在Promise返回函数中find第一个成功

给定一些function,返回承诺:

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()不能使用,因为函数不能全部被触发。 他们必须连续执行,一次成功后停止。

你说你的第一个问题实际上只是第二个问题,这是真正的问题。

所以我认为你的问题是:你如何执行一系列的函数来返回承诺串行,当第一个解决非null值的短路?

我可能不会,我会使用reject而不是resolve(null) (但在你已经澄清你要resolve(null)的评论resolve(null) ,我看到你的观点,我覆盖下面):

 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 } }); } 

然后你使用catch来处理被拒绝的事情,直到你得到一个解决scheme:

 foo("a") .catch(() => bar(1)) .catch(() => foo("b")) .catch(() => bar(2)) .catch(() => foo("c")) .catch(() => bar(3)) .then(value => { console.log("Done", value); }); 
 function otherStuff(arg) { return arg == 2; } function stuff(arg) { return arg == "c"; } function foo(arg) { console.log("foo:", arg); return new Promise(function(resolve, reject) { if (stuff(arg)) { console.log("foo:", arg, "resolving"); resolve('result from foo'); } else { console.log("foo:", arg, "rejecting"); reject(); // <=== Note } }); } // ... maybe more of these functions ... function bar(arg) { console.log("bar:", arg); return new Promise(function(resolve, reject) { if (otherStuff(arg)) { console.log("bar:", arg, "resolving"); resolve('result from bar'); } else { console.log("bar:", arg, "rejecting"); 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); }); 

我不认为有任何预build的东西。 你可以创build自己的工作,而无需太多的工作。 假设你有一个函数数组,在调用时返回promise。 然后,您可以遍历该数组,并在获得您喜欢的结果时停止。 当序列中的承诺被拒绝时,你不知道你想做什么,这个实现继续到下一个函数,但是你可以编写你想要的任何行为:

 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 }); 

工作演示: https : //jsfiddle.net/jfriend00/fwr03f7q/

从@TJCrowder和@ jfriend00的答案中找出答案。

TL; DR:

 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) 

目标:迭代通过返回Promises的函数,直到我们成功地resolve了一个值,之后我们短路。 我们不想要Promise.race ,而是连续运行函数。

看到这个片段为一个完整的工作示例:

 /* Define functions which return Promises honouring the following contract: * switch (state) { * case success: * resolve(result); * break; * case failure: * resolve(null); * break; * case error: * reject(error); * } */ const functions = [ arg => new Promise((resolve) => { console.log('checking a against', arg); if (arg === 'a') { resolve('A'); } else { resolve(); } }), arg => new Promise((resolve) => { console.log('checking b against', arg); if (arg === 'b') { resolve('B'); } else { resolve(); } }), // Intentionally omit handling 'c' arg => new Promise((resolve, reject) => { console.log('checking d against', arg); if (arg === 'd') { console.log('simulating error'); reject(new Error('D')); } else { resolve(); } }), arg => new Promise((resolve) => { console.log('checking e against', arg); if (arg === 'e') { resolve('E'); } else { resolve(); } }) ]; /* Successively call functions with given arg until we resolve a value, * after which we short-circuit. */ function delegate(arg) { console.log('\nDELEGATING for', arg); functions.reduce( // Note that this null comparison always happens N times, // where N is the number of functions // (unless one of the functions rejects) (p, fn) => p.then(r => r ? r : fn(arg)), Promise.resolve(null) ).then(value => { console.log('Done:', value); }) .catch(error => { console.log('Error:', error); }); } // Run sample input through the delegate function ['a', 'b', 'c', 'd', 'e'].forEach(function(e, i) { setTimeout(delegate.bind(null, e), i * 100); // delay, for printing });