【问题标题】:typescript: How to check functions are run in parallel打字稿:如何检查函数是否并行运行
【发布时间】:2021-05-30 23:10:36
【问题描述】:

我有一个简单的for 循环,如下所示:

  private async loadImages(urls: string[]): Promise<void> {
    await Promise.all(urls.map(async (url) => {
      this.loadImage(url);
    }));
  }

loadImage 原型在哪里:

private async loadImage(url: string): Promise<Image>

如果我对这个post 的理解是正确的,那么loadImages 函数会并行调用loadImage(s)。然而,在这里查看 chome 网络选项卡是我所看到的(呼叫似乎是级联完成的,好像一个正在等待前一个):

是否有工具可以调试发生了什么问题?是否有一个简单的检查来验证loadImage 调用是否确实处于“并行”状态?

【问题讨论】:

标签: typescript promise


【解决方案1】:

您应该将其与:

private async loadImages(urls: string[]): Promise<void> {
  for(url of urls) {
    await this.loadImage(url);
  }
}

该调用确实会在开始下一个承诺之前等待每个承诺成功,并且在完成的总时间上应该要差得多。

在更一般的意义上:您无法获得无限并发。 JavaScript 是单线程的,并行启动 100 个请求实际上可能会使您的代码变慢,因为您同时打开了太多请求。 (您最终可能会在没有意识到的情况下进行拒绝服务攻击。)

将您的 url 分块为一组 5 或 10 个 url 并在每个块上执行 Promise.all 可能是有意义的。 (或者使用 Promise 库,您可以在其中设置并发,例如 Bluebird)。


我做了一个测试用例,但请记住,我使用的是 nodejs 而不是浏览器,所以你的里程可能会有所不同。我比较了所有并行、迭代和分块方法。全并行是迄今为止最快的:

  • 并行:345.985ms
  • 分块:1633.872ms
  • 迭代:6681.045ms

这是我的代码:

import fetch from 'node-fetch';

const parallel = async (urls: string[]): Promise<void> => {
    await Promise.all(
        urls.map(async (url) => {
            return fetch(url);
        })
    );
}

const chunk = <T>(array: T[], size: number = 5): T[][] =>
    Array.from({length: Math.ceil(array.length / size)}, (value, index) => array.slice(index * size, index * size + size));


const chunked = async (urls: string[]): Promise<void> => {
    const chunkedUrls = chunk(urls);


    for (const chunk of chunkedUrls) {
        await Promise.all(chunk.map(url => fetch(url)));
    }
}


const iterative = async (urls: string[]): Promise<void> => {
    for (const url of urls) {
        await fetch(url);
    }
}

const compare = async () => {
    const urls = [...Array(100)].map(() => `http://placekitten.com`);

    console.time('parallel');
    await parallel(urls);
    console.timeEnd('parallel');

    console.time('chunked');
    await chunked(urls);
    console.timeEnd('chunked');

    console.time('iterative');
    await iterative(urls);
    console.timeEnd('iterative')

    return 'completed';
}

compare()
    .then(console.log)
    .catch(console.error);

【讨论】:

    【解决方案2】:

    一切都在做它应该做的事情。由于 javascript 是单线程,因此您无法并行执行某些操作。 尽管如此,promise 是异步的,但这与并行不同。如果您想了解更多关于异步函数如何在单线程中工作的信息,我可以建议this post.

    所以Promise.all 不会像您想象的那样产生新线程。相反,它返回一个单一的 Promise,当所有输入的 Promise 都解决时解决,或者在输入的 Promise 之一被拒绝时立即拒绝。 Here 你可以阅读更多关于 Promise.all 函数的信息。


    在 ritaj 对我的回答发表评论后,我做了一个测试以确保:

    await Promise.all(
        Array(200).fill(0).map(() => fetch('https://ghibliapi.herokuapp.com/films'))
    );
    

    我在运行此代码时查看了浏览器的线程数(抱歉 herokuapp 有 200 个请求......)。它保持不变。因此请求必须是按顺序执行的。

    【讨论】:

    • 这些都不是真的。网络请求不在主线程上执行,绝对可以并行运行。
    • This question 对 asyncthreads 等有很好的回答。
    • @ritaj 你确定吗?据我所知,它们确实是并发的,但这不是并行的。为什么网络事件看起来像“瀑布”?
    猜你喜欢
    • 2018-09-23
    • 2014-06-19
    • 2021-01-31
    • 2018-05-22
    相关资源
    最近更新 更多