[英]How to chain promises on reject
给定一个函数, fn
,它返回一个promise,以及一个任意长度的数据数组(例如data = ['apple', 'orange', 'banana', ...]
)你如何在每个元素上链接函数调用序列中的数组,如果fn(data[i])
解析,整个链完成并停止调用fn
,但如果fn(data[i])
拒绝,则执行下一个调用fn(data[i + 1])
?
这是一个代码示例:
// this could be any function which takes input and returns a promise
// one example might be fetch()
const fn = datum =>
new Promise((resolve, reject) => {
console.log(`trying ${datum}`);
if (Math.random() < 0.25) {
resolve(datum);
} else {
reject();
}
});
const foundResult = result => {
// result here should be the first value that resolved from fn(), and it
// should only be called until the first resolve()
console.log(`result = ${result}`);
};
// this data can be purely arbitrary length
const data = ['apple', 'orange', 'banana', 'pineapple', 'pear', 'plum'];
// this is the behavior I'd like to model, only for dynamic data
fn('apple').then(foundResult)
.catch(() => {
fn('orange').then(foundResult)
.catch(() => {
fn('banana').then(foundResult)
.catch(() => {
/* ... and so on, and so on ... */
});
});
});
我觉得也许这种模式的优雅解决方案是我所缺少的。 这个行为与Array.some()
非常相似,但是我试图摆弄它是空洞的。
编辑:我从数字数据切换到字符串,以强调解决方案不需要依赖于数字数据。
编辑#2:为了进一步澄清, fn
可以是任何接受输入并返回promise的函数。 上面的fn
实现只是为了给出一个完整的例子。 实际上, fn
实际上可能类似于API请求,数据库查询等。
您可以使用async/await
和循环:
async function search() {
for (let item of data) {
try {
return await fn(item);
} catch (err) { }
}
throw Error ("not found");
}
search().then(foundResult).catch(console.log);
fn
可以返回Promise(等待)或只返回一个值(返回) data
可能是无限iterable
序列(生成器) 如果序列失败,这是输出:
trying apple
trying orange
trying banana
trying pineapple
trying pear
trying plum
Error: not found
您可以使用Array.reduce来获取所需的数据。
data.reduce((promise, item) => promise.then(
(param) => {
if (param) return Promise.resolve(param);
return fn(item).catch(() => Promise.resolve());
}
), Promise.resolve())
.then(foundResult)
基本上它会在结果通过后将结果传递给结束。 如果fn
失败,它将通过未定义值的promise传递给下一个链以触发fn
。
编写如下搜索功能:
function search(number) {
if (number < data.length) {
fn(data[number]).then(foundResult)
.catch(() => search(number + 1));
}
}
search(0);
你可以编写一个非常简单的递归函数,它将在第一个解析时停止并在catch上递归。
function find_it(arr) {
let [head, ...rest] = arr
if (!head) return console.log("not found") // all rejects or no data
fn(head)
.then(r => foundResult(r) )
.catch(r => find_it(rest))
}
find_it(data)
如果找到匹配并且不关心data
长度,除非超过递归中的堆栈大小,否则这有利于在第一次匹配时停止而不调用所有值。 当然,当所有承诺都拒绝做某事时,修改边缘情况的动作会很容易。
找到结果:
$ node ./test
尝试苹果
尝试橙色
结果=橙色
并没有找到:
$ node ./test尝试苹果
尝试橙色
尝试香蕉
尝试菠萝
尝试梨
尝试梅花
未找到
你想要做的就是这样做我猜(它不是严格等同的,见下面的储备):
const fn = n => new Promise(() => {});
const seq = [];
const process = n => fn(n).then(foundResult);
seq.slice(1).reduce((operation, item) => {
return operation
.catch(() => process(item));
}, process(seq[0]));
使用Promise.race :
const fetchAll = ( urls = [] ) => Promise.race(urls.map(fn)).then(foundResult);
但是我不确定这是你想要实现的:例如在你的代码片段中你没有在catch
处理程序中返回任何内容,这意味着foundResult
很可能是副作用。 此外,在阅读代码片段时,您正在捕获可以从foundResult
内部而不是fn
函数中引发的错误。
作为一个经验法则,我总是试图通过已知的“类型”履行我的承诺,或者拒绝错误。 在您的示例中,不清楚您生成的承诺将以任何值结算,无论是rejectionValue
还是fulfillmentValue
。
也许如果你提供一个用例,我可以帮助你一点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.