繁体   English   中英

JavaScript - Promise.allSettled + Array.reduce()

[英]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,其中包含userIdlanguage的属性,则此步骤是不必要的。)然后我们将生成的承诺分开,以将已fulfilled的承诺与已rejected的承诺分开。 我这样做是因为您的问题没有说明如何处理被拒绝的语言查找。 我选择向 output 添加一个条目。这里除了esen之外,我们还在_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM