使用promise.all和.then

问题描述 投票:0回答:2

我想将

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
得到解决。因此我不会实现并发。

我的理解正确吗?为什么/为什么不?

node.js typescript async-await axios promise
2个回答
0
投票

我建议您运行以下代码来亲自查看行为:

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 } ]

0
投票

因此,承诺实际上不会被汇总并在

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);
}
© www.soinside.com 2019 - 2024. All rights reserved.