代码之家  ›  专栏  ›  技术社区  ›  maja

使用Array.reduce()从fetch承诺提取JSON数据

  •  0
  • maja  · 技术社区  · 4 年前

    我在写 then() 用于从响应数组中提取json数据的语句 fetch() . 在下面的代码中 queries 是一系列调用返回的承诺数组 获取() . 我使用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是 改为承诺 .

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

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

    2 回复  |  直到 4 年前
        1
  •  2
  •   CertainPerformance    4 年前

    使累加器的初始值是一个可以解析为空数组的承诺,然后 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 打电话给 .json() 相反,为了更快地产生结果:

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

    如果查询是 Response 是从 fetch ,最好将 .json() 呼唤原作 取来 改为打电话,例如:

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

    这样,您就可以使用响应 立即 当他们回来,而不是等待 全部的 在开始处理第一个之前返回的响应。

        2
  •  3
  •   Kobe    4 年前

    虽然这不是你要求的,但你可以避免不得不使用的痛苦 reduce ,并利用 Promise.all() 您已经在使用:

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

    这是一个很短的方式和较少的头痛阅读时,你回来。