给定一个函数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
可以是任何接受输入并返回承诺的函数。上面的fn
实现只是为了给出一个完整的例子。实际上,fn
实际上可能类似于API请求,数据库查询等。
答案 0 :(得分:5)
您可以使用reset()
和循环:
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);
可以返回Promise(等待)或只返回值(返回)fn
可能是无限data
序列(生成器)这是序列失败时的输出:
iterable
在es2017中支持异步是原生的,但可以使用babel或typescript
转换为es3 / es5答案 1 :(得分:4)
您可以使用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
失败,它会将未定义的值承诺传递给下一个链,以触发fn
。
答案 2 :(得分:2)
编写如下搜索功能:
function search(number) {
if (number < data.length) {
fn(data[number]).then(foundResult)
.catch(() => search(number + 1));
}
}
search(0);
答案 3 :(得分:1)
你可以编写一个非常简单的递归函数,它将在第一个解析时停止并递归到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 尝试苹果
尝试橙色 尝试香蕉
尝试菠萝
尝试梨 尝试梅 找不到
答案 4 :(得分:0)
你想要做的事情可以这样做我猜(它不是严格等同,见下面的储备):
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
函数中引发的错误。
作为一个经验法则,我总是试图通过一个已知的&#34;类型&#34;来实现我的承诺。或者因错误而被拒绝。您的示例中不清楚您生成的承诺将以任何值结算,无论是rejectionValue
还是fulfillmentValue
。
也许如果您提供一个用例,我可以帮助您一点。