给定一些函数,返回承诺:
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(),因为函数不能全部被触发。它们必须连续执行,在第一次成功后停止。
答案 0 :(得分:3)
您已经说过,您的第一个问题实际上只是设置了第二个问题,这是真正的问题。
所以我认为你的问题是:你如何执行一系列串行返回promises的函数,当第一个函数以非null
值解析时会短路?
我可能不会,我会使用 reject
而不是resolve(null)
(但在评论中,您已经澄清了您想要的{\ n} {1}},我明白了你的意思;我将在下面介绍一下):
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
foo("a")
.catch(() => bar(1))
.catch(() => foo("b"))
.catch(() => bar(2))
.catch(() => foo("c"))
.catch(() => bar(3))
.then(value => {
console.log("Done", value);
});
&#13;
这是有效的,因为分辨率会绕过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);
});
处理程序,因此永远不会调用后续函数。
如果你有一系列要调用的函数,那就有一个成语:catch
:
Array#reduce
let functions = [
() => foo("a"),
() => bar(1),
() => foo("b"),
() => bar(2),
() => foo("c"),
() => bar(3)
];
functions.reduce((p, fn) => p.catch(fn), Promise.reject())
.then(value => {
console.log("Done", value);
});
&#13;
正如您可能知道的那样,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
}
});
}
let functions = [
() => foo("a"),
() => bar(1),
() => foo("b"),
() => bar(2),
() => foo("c"),
() => bar(3)
];
functions.reduce((p, fn) => p.catch(fn), Promise.reject())
.then(value => {
console.log("Done", value);
});
对于减少&#34;一个值的数组,例如一个简单的总和:
Array#reduce
在上面,对于&#34;总和&#34;等价,我们从被拒绝的承诺开始,并使用[1, 2, 3].reduce((sum, value) => sum + value, 0); // 6
来创建承诺链。调用catch
的结果是来自reduce
的最后一个承诺。
但,如果您想使用catch
,请以类似的方式使用resolve(null)
:
then
foo("a")
.then(result => result ? result : bar(1))
.then(result => result ? result : foo("b"))
.then(result => result ? result : bar(2))
.then(result => result ? result : foo("d"))
.then(result => result ? result : bar(3))
.then(value => {
console.log("Done", value);
});
&#13;
或者使用数组:
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, "resolving null");
resolve(null);
}
});
}
// ... 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, "resolving null");
resolve(null);
}
});
}
foo("a")
.then(result => result ? result : bar(1))
.then(result => result ? result : foo("b"))
.then(result => result ? result : bar(2))
.then(result => result ? result : foo("d"))
.then(result => result ? result : bar(3))
.then(value => {
console.log("Done", value);
});
let functions = [
() => foo("a"),
() => bar(1),
() => foo("b"),
() => bar(2),
() => foo("c"),
() => bar(3)
];
functions.reduce((p, fn) => p.then(result => result ? result : fn()), Promise.resolve(null))
.then(value => {
console.log("Done", value);
});
&#13;
这是有效的,因为如果我们返回一个真值(或者你可以使用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, "resolving null");
resolve(null);
}
});
}
// ... 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, "resolving null");
resolve(null);
}
});
}
let functions = [
() => foo("a"),
() => bar(1),
() => foo("b"),
() => bar(2),
() => foo("c"),
() => bar(3)
];
functions.reduce((p, fn) => p.then(result => result ? result : fn()), Promise.resolve(null))
.then(value => {
console.log("Done", value);
});
),我们将该结果返回到链中,这意味着result => result !== null ? result : nextCall()
返回一个已解决的带有该值的promise;但是如果我们得到一个假值,我们调用下一个函数并返回它的诺言。
正如你所看到的,这有点冗长,这也是承诺在解决和拒绝之间有所区别的部分原因。
答案 1 :(得分:2)
我认为没有任何预先建造的东西。你可以创建自己的工作而不需要太多工作。假设你有一个函数数组,在调用时返回promises。然后,您可以遍历该数组,并在获得所需结果时停止。当序列中的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
});
答案 2 :(得分:1)
感谢来自@ T.J.Crowder和@ jfriend00的答案。
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
});
我使用建议的reject(null)
策略实现了相同的示例,而不是resolve(null)
,表示非错误失败,不应该停止迭代函数:
/* Define functions which return Promises honouring the following contract:
* switch (state) {
* case success:
* resolve(result);
* break;
* case failure:
* reject(null); // << NOTE
* break;
* case error:
* reject(error);
* }
*/
const functions = [
arg => new Promise((resolve, reject) => {
console.log('checking a against', arg);
if (arg === 'a') {
resolve('A');
} else {
reject();
}
}),
arg => new Promise((resolve, reject) => {
console.log('checking b against', arg);
if (arg === 'b') {
resolve('B');
} else {
reject();
}
}),
// 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 {
reject();
}
}),
arg => new Promise((resolve, reject) => {
console.log('checking e against', arg);
if (arg === 'e') {
resolve('E');
} else {
reject();
}
})
];
/* 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(
// Check for error, or just rejection without value.
// Note that this check happens N-1 times,
// where N is the number of functions until one resolves
(p, fn) => p.catch(e => e ? Promise.reject(e) : fn(arg)),
Promise.reject()
).then(value => {
console.log('Done:', value);
})
.catch(error => {
console.log('Error:', error);
});
}
['a', 'b', 'c', 'd', 'e'].forEach(function(e, i) {
setTimeout(delegate.bind(null, e), i * 100); // delay, for printing
});
注意,如果您阅读@ T.J.Crowder的优秀示例,我们必须在catch
函数中添加错误检查:
(p, fn) => p.catch(e => e ? Promise.reject(e) : fn(arg))
如果我们刚刚
(p, fn) => p.catch(() => fn(arg))
或
(p, fn) => p.catch(fn.bind(null, arg))
我们会默默地吸收错误(reject(error)
),并继续,好像我们只是遇到了非错误的错误。
考虑到这一点,我们通过使用reject(null)
样式最终得到更清晰,更有效(平均)的代码。