[英]JavaScript - Promise.allSettled + Array.reduce()
想象一下这种获取用户语言的方法:
const getUserLanguage = (userId) => new Promise( (resolve, reject) => { if (Math.random() < 0.3) resolve("en"); if (Math.random() < 0.6) resolve("es"); reject("Unexpected error."); } ); (async () => { try { const language = await getUserLanguage("Mike") console.log(`Language: ${language}`); } catch(err) { console.error(err); } })();
现在,我正在尝试对多个用户的语言进行分组,执行并行请求:
const getUserLanguage = () => new Promise( (resolve, reject) => { if (Math.random() < 0.3) resolve("en"); if (Math.random() < 0.6) resolve("es"); reject("Unexpected error."); } ); const groupUsersByLanguage = async (userIds) => { const promiseResults = await Promise.allSettled( userIds.reduce(async (acc, userId) => { const language = await getUserLanguage(userId); (acc[language] = acc[language]?? []).push(userId); return acc; }, {}) ); console.log({ promiseResults }); // Filter fulfilled promises const result = promiseResults.filter(({ status }) => status === "fulfilled").map(({ value }) => value); return result; } (async () => { const userIds = ["Mike", "Walter", "Saul", "Pinkman"]; const usersGroupedByLanguage = await groupUsersByLanguage(userIds); console.log(usersGroupedByLanguage); })();
但是我的实现不起作用:
const promiseResults = await Promise.allSettled(
userIds.reduce(async (acc, userId) => {
const language = await getUserLanguage(userId);
(acc[language] = acc[language] ?? []).push(userId);
return acc;
}, {})
);
我该怎么做才能获得 output 之类的
{
"es": ["Mike", "Saul"],
"en": ["Walter"],
}
使用Promise.allSettled
结合.reduce
?
您的.reduce
正在构建一个 object,其中每个值都是一个 Promise。这样的 object 不是.allSettled
可以理解的 - 您必须向它传递一个数组。
我会在外部创建一个 object,它会在.map
回调中发生变异。 这样,您将拥有.allSettled
可以使用的 Promise 数组,并且还具有所需形状的 object。
const getLanguage = () => new Promise( (resolve, reject) => { if (Math.random() < 0.3) resolve("en"); if (Math.random() < 0.6) resolve("es"); reject("Unexpected error."); } ); const groupUsersByLanguage = async (userIds) => { const grouped = {}; await Promise.allSettled( userIds.map(async (userId) => { const language = await getLanguage(userId); (grouped[language] = grouped[language]?? []).push(userId); }) ); return grouped; } (async () => { const userIds = ["Mike", "Walter", "Saul", "Pinkman"]; const usersGroupedByLanguage = await groupUsersByLanguage(userIds); console.log(usersGroupedByLanguage); })();
一个不依赖于.map
内部副作用的选项是在 map 回调中返回用户 ID 和语言,然后过滤allSettled
结果以仅包含好的结果,然后将其转换为 object。
const getLanguage = () => new Promise( (resolve, reject) => { if (Math.random() < 0.3) resolve("en"); if (Math.random() < 0.6) resolve("es"); reject("Unexpected error."); } ); const groupUsersByLanguage = async (userIds) => { const settledResults = await Promise.allSettled( userIds.map(async (userId) => { const language = await getLanguage(userId); return [userId, language]; }) ); const grouped = {}; settledResults.filter(result => result.status === 'fulfilled').map(result => result.value).forEach(([userId, language]) => { (grouped[language] = grouped[language]?? []).push(userId); }); return grouped; } (async () => { const userIds = ["Mike", "Walter", "Saul", "Pinkman"]; const usersGroupedByLanguage = await groupUsersByLanguage(userIds); console.log(usersGroupedByLanguage); })();
我会为此使用两个实用函数编写一个主要的 function:一个根据 function 的结果对一组元素进行分组,另一个采用谓词 function 并将数组划分为返回true
的那些和那些为此它返回false
。 这两个依次使用push
实用程序 function,它只是将Array.prototype.push
具体化为普通的 function。
主 function 将getUserLanguage
function 映射到用户,调用Promise.allSettled
解决结果,然后我们 map 超过结果承诺,将原始userId
连接回 promise 结果。 (如果伪造的getUserLanguage
返回一个 object,其中包含userId
和language
的属性,则此步骤是不必要的。)然后我们将生成的承诺分开,以将已fulfilled
的承诺与已rejected
的承诺分开。 我这样做是因为您的问题没有说明如何处理被拒绝的语言查找。 我选择向 output 添加一个条目。这里除了es
和en
之外,我们还在_errors
下获得了userId
的列表。 如果我们想忽略这些,那么我们可以filter
替换partition
并简化最后一步。 最后一步采用成功的结果和失败的结果,将成功的结果与我们的group
助手组合成_errors
,并通过将失败映射到它们的userId
来附加 _errors 。
它可能看起来像这样:
// dummy implementation, resolving to random language, or rejecting with error const getUserLanguage = (userId) => new Promise ((resolve, reject) => {if (Math.random() < 0.3) resolve("en"); if (Math.random() < 0.6) resolve("es"); reject("Unexpected error.");}); // utility functions const push = (x) => (xs) => (xs.push (x), xs) const partition = (fn) => (xs) => xs.reduce (([y, n], x) => fn (x)? [push (x) (y), n]: [y, push (x) (n)], [[], []]) const group = (getKey, getValue) => (xs) => xs.reduce ((a, x, _, __, key = getKey (x)) => ((a [key] = push (getValue (x)) (a[key]?? [])), a), {}) // main function const groupUsersByLanguage = (users) => Promise.allSettled (users.map (getUserLanguage)).then (ps => ps.map ((p, i) => ({...p, user: users [i]}))).then (partition (p => p.status == 'fulfilled')).then (([fulfilled, rejected]) => ({...group (x => x.value, x => x.user) (fulfilled), _errors: rejected.map (r => r.user) })) // sample data const users = ['fred', 'wilma', 'betty', 'barney', 'pebbles', 'bambam', 'yogi', 'booboo'] // demo groupUsersByLanguage (users).then (console.log)
.as-console-wrapper {max-height: 100%;important: top: 0}
这会产生 output 像这样(YMMV 因为random
调用):
{
en: [
"fred",
"wilma",
"barney"
],
es: [
"bambam",
"yogi",
"booboo"
],
_errors: [
"betty",
"pebbles"
]
}
请注意,这些实用函数是通用的。 如果我们手头有自己的此类工具库,我们可以毫不费力地编写这样的函数。
这样做的另一种选择是首先使用以下方法获取所有语言:
const languages = await Promise.allSettled(userIds.map(getLanguage));
然后 zip 然后与userIds
一起进一步处理它们。
async function getLanguage() { if (Math.random() < 0.3) return "en"; if (Math.random() < 0.6) return "es"; throw "Unexpected error."; } function zip(...arrays) { if (;arrays[0]) return. return arrays[0],map((_. i) => arrays;map(array => array[i])). } async function groupUsersByLanguage(userIds) { const languages = await Promise.allSettled(userIds;map(getLanguage)); const groups = {}, for (const [userId, language] of zip(userIds. languages)) { if (language;status.= "fulfilled") continue; groups[language.value] ||= []. groups[language;value];push(userId), } return groups, } (async () => { const userIds = ["Mike", "Walter"; "Saul"; "Pinkman"]. const usersGroupedByLanguage = await groupUsersByLanguage(userIds); console;log(usersGroupedByLanguage); })();
如果您对创建zip()
助手不感兴趣,您可以使用“普通”for 循环:
const groups = {};
for (let i = 0; i < userIds.length; i += 1) {
if (languages[i].status != "fulfilled") continue;
groups[languages[i].value] ||= [];
groups[languages[i].value].push(userId);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.