【问题标题】:Javascript: mixed serial and parallel processingJavascript:混合串行和并行处理
【发布时间】:2020-12-22 22:34:33
【问题描述】:

我正在为我的问题寻找 ES6 解决方案(我想这可能很常见......):

我有一个远程提供商列表,每个提供商在一个页面上发布一个项目列表,每个项目的详细信息在其他页面上。 我需要从所有提供商那里获取所有项目的详细信息,每小时一次(比如说)。

我想并行获取所有提供者列表,并且在解决每个列表后立即从该列表中并行获取所有项目详细信息(我将根据需要限制请求以避免ECONNRESET 错误,但这是另一个故事)。

这是我的暂定解决方案的实现:

    const providers = [
      {
        name: 'A',
        urlList: 'https://jsonplaceholder.typicode.com/users/?_start=0&_limit=1',
        urlItem: 'https://jsonplaceholder.typicode.com/posts?userId=%id%&_start=0&_limit=2',
      },
      {
        name: 'B',
        urlList: 'https://jsonplaceholder.typicode.com/users/?_start=2&_limit=2',
        urlItem: 'https://jsonplaceholder.typicode.com/posts?userId=%id%&_start=0&_limit=2',
      },
      {
        name: 'C',
        urlList: 'https://jsonplaceholder.typicode.com/users/?_start=4&_limit=2',
        urlItem: 'https://jsonplaceholder.typicode.com/posts?userId=%id%&_start=0&_limit=2',
      },
    ];
    
    const getList = (provider) => fetch(provider.urlList).then(res => res.json());
    
    const getItems = (provider, list) => Promise.all(
      list.map(item => {
        return fetch(provider.urlItem.replace("%id%", item.id)).then(res => res.json()).then(res => res.map(r => ({ provider: provider.name, name: item.name, ...r })));
      })
    );
    
    const providerFetch = async provider => {
      console.log(`providerFetch ${provider.name} start`);
      try {
        const list = await getList(provider);
        const items = await getItems(provider, list);
        return items;
      } catch (error) {
        console.error(`providerFetch ${provider.name} error: ${error}`);
      } finally {
        console.log(`providerFetch ${provider.name} done`);
      }
    }
    
    (async () => {
      console.log('start');
      const data = await Promise.all(
        providers.map(async provider => {
          const providerData = providerFetch(provider);
          return providerData;
        })
      );
      //console.dir(data, { depth: 3 });
      console.log(data.flat(2));
      console.log('end');
    })();

它至少有两个问题:

  1. 我使用Promise.all() 两次,所以我得到一个3 维数组,而我更喜欢一个简单的一维数组...有没有办法让Promise.all() 连接承诺结果而不是推送它们?
  2. 最严重的问题:我正在获取所有供应商列表,并且只有在所有供应商都准备好后,我才开始获取项目(按预期并行)。相反,我想在获取列表后立即开始为每个列表获取项目。你能给我一些提示如何改变我的代码来实现它吗?

更新: 还要感谢@Bergi 的 cmets,我可以理解:

  • 第 1 点:这可以通过在最终数据上使用flat(2)(2 是要展平的深度)简单地解决;该解决方案不是那么圆滑,但看起来promise.All() 只能推送,不能连接。我确实更改了我的代码示例以显示它。
  • 第 2 点:providerFetch 连续工作的事实只是我的一个假设,可能是因为getLists 在这个测试代码中是如此之快......事实上,添加了延迟在getList 返回之前,我可以看到一些getItems 在所有getLists 完成之前开始。

【问题讨论】:

  • "我正在获取所有供应商列表,并且只有在所有供应商都准备好后,我才开始获取项目" - 嗯,不,你没有。您的providerFetchgetList 完成后立即调用getItems。您的代码中没有任何内容等待所有列表。
  • "我使用了两次Promise.all(),所以我得到了一个 3 维数组" - 嵌套两个 Promise.alls 可以得到一个二维数组。第三维来自getItems获取数组。
  • "有没有办法让 Promise.all() concat 承诺结果" - 不,没有,但是在 Promise.all() 承诺履行后展平嵌套数组很简单(在.then 回调或await 之后)。

标签: javascript node.js asynchronous ecmascript-6 es6-promise


【解决方案1】:

Promise.all 本身没有 concat 功能,但 javascript 数组有。因此,只需将结果连接起来:

const result = await Promise.all(/* your original code ... */);

let data = [];

result.forEach(x => data = data.concat(x));

// now data is as you expect

【讨论】:

  • providerFetch 不(也不能)返回数组
  • @Bergi 还没写完答案
  • @Bergi 没有注意到它正在等待两个系列的承诺
猜你喜欢
  • 2015-11-28
  • 1970-01-01
  • 2017-01-27
  • 1970-01-01
  • 2016-01-20
  • 1970-01-01
  • 2015-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多