我想将
await Promise.all
与 axios 一起使用,以便可以同时发送请求。我有一个地图,其中键作为 id,值作为 url。我想在类似的映射中保留输出,键仍然是 id,但值将是响应(arraybuffer)。
以下是法学硕士给我的建议:
async function fetchImages(
idUrlMap: Map<string, string>
): Promise<Map<string, ArrayBuffer>> {
// Convert the map into an array of promises
const promises = Array.from(idUrlMap.entries()).map(([id, url]) => {
return axios
.get(url, { responseType: 'arraybuffer' })
.then(response => ({ id, data: response.data }));
});
// Resolve all promises concurrently
const results = await Promise.all(promises);
// Convert the results back into a map
const resultMap = new Map<string, ArrayBuffer>();
results.forEach(({ id, data }) => {
resultMap.set(id, data);
});
return resultMap;
}
我想知道的是——我们正在为每个数组元素执行 axios.get().then() ,这将在准备好后立即解决承诺。因此,这些承诺实际上不会被汇总并在
Promise.all
得到解决。因此我不会实现并发。
我的理解正确吗?为什么/为什么不?
我建议您运行以下代码来亲自查看行为:
async function fetchImages(idUrlMap) {
// Convert the map into an array of promises
const promises = Array.from(idUrlMap.entries()).map(([id, url]) => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(id, 'Triggered timeout');
resolve();
}, 3000-id*1000)
}).then(() => {
console.log('In then for', id);
return { id }
})
});
// Resolve all promises concurrently
const results = await Promise.all(promises);
console.log('Results finished', results);
}
fetchImages(Object.entries({'1': 'test1', '2': 'test2', '3': 'test3'}));
这是您的简化版本,它使用
setTimeout
而不是 axios
,但适用相同的原则。您会注意到 results
最终成为 then
回调中返回的值的数组。换句话说,一切都按照您的预期运行,无需过多思考。
预期输出:
2 Triggered timeout
In then for 2
1 Triggered timeout
In then for 1
0 Triggered timeout
In then for 0
Results finished [ { id: 0 }, { id: 1 }, { id: 2 } ]
因此,承诺实际上不会被汇总并在
: 得到解决Promise.all
这是真的:
Promise.all
所做的就是创建一个新的 Promise,其分辨率取决于这些单独 Promise 的分辨率。
因此我不会实现并发。
如果您所说的并发指的是 HTTP 请求,那么它们都已启动,并且任何实现这些请求和检索响应的非 JavaScript 逻辑原则上都可以并发运行。请注意,代码片段中的 JavaScript 代码不会同时运行。一旦执行完遇到的第一个
await
,它就会完成该函数,然后 JS 引擎将监视其队列并在 Promise 解析时执行 then
回调。最后一个要解决的是 Promise.all
返回的承诺。然后 fetchImages
可以恢复并执行它的 return
语句。这将解决 fetchImages
的调用者收到的承诺。
对代码的一些评论:
.entries()
来迭代它,因为地图本身是可迭代的。Array.from
创建一个数组,然后在其上链接一个 map
。相反,请使用 Array.from
的第二个参数来执行映射。Map
构造函数,并避免最后的循环。改编代码:
async function fetchImages(idUrlMap: Map<string, string>): Promise<Map<string, ArrayBuffer>> {
// No need to call .entries(). You can use the second argument of Array.from:
const promises = Array.from(idUrlMap, ([id, url]) => {
return axios
.get(url, { responseType: 'arraybuffer' })
// Map the response to key/value pairs
.then(response => [id, response.data]);
});
// Promise.all does not resolve the promises. It merely creates a new
// promise that will resolve when all the given promises have resolved.
const results = await Promise.all(promises);
// Use the Map constructor argument to create the Map:
return new Map<string, ArrayBuffer>(results);
}