使用 Array.reduce() 从提取承诺中提取 JSON 数据
Extracting JSON data from fetch promises using Array.reduce()
我正在编写一个 then()
语句,用于从 fetch()
的响应数组中提取 json 数据。在下面的代码中 queries
是一系列调用 fetch()
返回的承诺数组。我正在使用 async/await 作为响应,因为否则承诺将在没有解决的情况下返回(我在 中找到了解决方案)。
我的第一次尝试工作正常,当我进入 jsonified
时,我获得了一个以 promises 作为元素的数组:
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;
}, [])
})
我可以毫无问题地使用第一个版本,程序运行正常,但是 Array.reduce()
里面发生了什么? 为什么要将承诺推入累加器 returns 承诺而不是数组? 我如何使用 Array.reduce()
重构代码?
让累加器的初始值成为解析为空数组的 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
生成的 Response
数组,将 .json()
调用链接到原始 fetch
会更好] 改为调用,例如:
const urls = [ ... ];
const results = await Promise.all(
urls.map(url => fetch(url).then(res => res.json()))
);
这样,您可以在响应返回时立即使用响应,而不必等待所有响应返回在开始处理第一个之前。
虽然这不是您所要求的,但您可以避免必须使用 reduce
的痛苦,而只需利用您已经在使用的 Promise.all()
:
return Promise.all(queries.map(q => q.then(res => res.json()))
.then(data => {...})
这是一种更短的方式,当您回过头来阅读它时也不那么头疼。
我正在编写一个 then()
语句,用于从 fetch()
的响应数组中提取 json 数据。在下面的代码中 queries
是一系列调用 fetch()
返回的承诺数组。我正在使用 async/await 作为响应,因为否则承诺将在没有解决的情况下返回(我在
我的第一次尝试工作正常,当我进入 jsonified
时,我获得了一个以 promises 作为元素的数组:
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;
}, [])
})
我可以毫无问题地使用第一个版本,程序运行正常,但是 Array.reduce()
里面发生了什么? 为什么要将承诺推入累加器 returns 承诺而不是数组? 我如何使用 Array.reduce()
重构代码?
让累加器的初始值成为解析为空数组的 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
生成的 Response
数组,将 .json()
调用链接到原始 fetch
会更好] 改为调用,例如:
const urls = [ ... ];
const results = await Promise.all(
urls.map(url => fetch(url).then(res => res.json()))
);
这样,您可以在响应返回时立即使用响应,而不必等待所有响应返回在开始处理第一个之前。
虽然这不是您所要求的,但您可以避免必须使用 reduce
的痛苦,而只需利用您已经在使用的 Promise.all()
:
return Promise.all(queries.map(q => q.then(res => res.json()))
.then(data => {...})
这是一种更短的方式,当您回过头来阅读它时也不那么头疼。