【问题标题】:Extracting JSON data from fetch promises using Array.reduce()使用 Array.reduce() 从 fetch Promise 中提取 JSON 数据
【发布时间】:2020-04-14 13:16:50
【问题描述】:

我正在编写一个then() 语句,用于从fetch() 的响应数组中提取json 数据。在下面的代码中,queries 是一系列对fetch() 的调用返回的承诺数组。我正在使用 async/await 作为响应,否则将返回承诺而不解决(我在 this question 中找到了解决方案)。

我的第一次尝试工作正常,当我推入jsonified 时,我获得了一个以承诺作为元素的数组:

return Promise.all(queries)
.then(async(responses)=> {
    let jsonified = [];
    for (let res of responses){
        jsonified.push(await(res.json()));
    }
    return jsonified;
}.then(data=> ...

但是当我进行重构并尝试使用Array.reduce() 时,我意识到当我推入累加器而不是获取一个以promise 作为元素的数组时,acc 被分配为promise.

.then(responses=> {
    return responses.reduce(async(acc, next) => {
        acc.push(await(next.json()));
        return acc;
    }, [])
})

我可以毫无问题地使用第一个版本并且程序可以正常运行,但是Array.reduce() 内部发生了什么? 为什么将 Promise 推入累加器会返回一个 Promise 而不是数组?我如何使用 Array.reduce() 重构代码?

【问题讨论】:

    标签: javascript arrays fetch async.js


    【解决方案1】:

    让累加器的初始值为解析为空数组的 Promise,然后在每次迭代中 await 累加器(以便所有先前的迭代在当前迭代运行之前解析)

    .then(responses=> {
        return responses.reduce(async (accPromiseFromLastIter, next) => {
           const arr = await accPromiseFromLastIter;
           arr.push(await next.json());
           return arr;
        }, Promise.resolve([]))
    })
    

    (也就是说,您的原始代码更清晰,我更喜欢它而不是.reduce 版本)

    现场演示:

    const makeProm = num => Promise.resolve(num * 2);
    
    const result = [1, 2, 3].reduce(async(accPromiseFromLastIter, next) => {
      const arr = await accPromiseFromLastIter;
      arr.push(await makeProm(next));
      return arr;
    }, Promise.resolve([]));
    
    result.then(console.log);

    除非您串行检索所有数据,否则请考虑使用Promise.all 来并行调用每个Promise 的.json(),以便更快地产生结果:

    return Promise.all(queries)
    .then(responses => Promise.all(responses.map(response => response.json())));
    

    如果查询是刚刚从fetch 生成的Responses 数组,则最好将.json() 调用链接到原始fetch 调用上,例如:

    const urls = [ ... ];
    const results = await Promise.all(
      urls.map(url => fetch(url).then(res => res.json()))
    );
    

    这样,您可以在响应返回时立即使用它们,而不必在开始处理第一个响应之前等待所有响应返回。

    【讨论】:

    • 我接受了最后一个建议,一旦它们单独准备好就通过 fetch 承诺。
    【解决方案2】:

    虽然这不是您所要求的,但您可以避免不得不使用reduce 的痛苦,而只需使用您已经在使用的Promise.all()

    return Promise.all(queries.map(q => q.then(res => res.json()))
      .then(data => {...})
    

    当你回到它时,它的阅读方式要短得多,也不那么令人头疼。

    【讨论】:

      猜你喜欢
      • 2017-04-30
      • 2016-11-17
      • 1970-01-01
      • 1970-01-01
      • 2019-03-18
      • 2020-02-28
      • 2017-06-08
      • 2022-11-07
      • 1970-01-01
      相关资源
      最近更新 更多