【问题标题】:How to prevent multiple http requests from firing all at once如何防止多个http请求同时触发
【发布时间】:2019-10-05 10:05:39
【问题描述】:

我有一个对象数组。对于每个对象,我需要触发一个异步请求(http 调用)。但我只想同时运行一定数量的请求。此外,如果我可以在所有请求完成后有一个同步点以执行某些代码,那将是很好的(但不是必需的)。

我尝试过以下建议:

Limit number of requests at a time with RxJS

How to limit the concurrency of flatMap?

Fire async request in parallel but get result in order using rxjs

还有更多...我什至尝试制作自己的运算符。

要么这些页面上的答案太旧而无法使用我的代码,要么我无法弄清楚如何将所有内容放在一起以使所有类型都很好地适应。

这是我目前所拥有的:

for (const obj of objects) {
  this.myService.updateObject(obj).subscribe(value => {
    this.anotherService.set(obj);
  });
}

编辑 1: 好的,我想我们快到了!随着 Juliuspschild 的回答(两者似乎同样有效),我设法限制了请求的数量。但现在它只会发射第一批 4 个,而不会发射其余的。所以现在我有:

const concurrentRequests = 4;
from(objects)
  .pipe(
    mergeMap(obj => this.myService.updateObject(obj), concurrentRequests),
    tap(result => this.anotherService.set(result))
  ).subscribe();

我对@9​​87654328@ 做错了吗?

顺便说一句:带有resultSelector 参数的mergeMap 已被弃用,所以我在没有它的情况下使用mergeMap。 还有,mergeMap中的objtap中是看不到的,所以只好用tap的参数

编辑 2:

确保您的观察者完成! (花了我一整天的时间)

【问题讨论】:

  • 是的,你的弃用是对的 :-) 我相应地更新了我的答案。你能看看我在 Stackblitz 上的例子吗?我无法重现您遇到的错误...也许您还可以创建一个显示错误的示例?
  • 我无法在 Stackblitz 中重现该问题。我认为这与 angular/electron/nodejs 没有任何关系……至少我希望如此。无论如何,这里有一个 Stackblitz,它与我的代码更相似:https://stackblitz.com/edit/rxjs-dawwsl?file=index.ts 我发誓我检查了代码字符的字符,但仍然......我唯一能说的是我的代码没有达到 finalize 方法。但是前 4 个请求都很好地通过了管道。
  • 哦,还有,控制台上没有错误。我怎样才能找出卡住的位置和原因?我试过 catchError 运算符 - 不走运。
  • 我想我疯了..我将 stackblitz 代码复制到我的应用程序中并且它可以工作...所以我可以排除我的堆栈电子/角度等。我创建了另一个 stackblitz这更接近我的代码:https://stackblitz.com/edit/angular-7-master-yf1cik 我已经尝试了 subscribe 方法中的错误函数......什么都没有
  • 不错!很高兴我能帮上忙!

标签: javascript angular typescript rxjs rxjs-pipeable-operators


【解决方案1】:

您可以使用mergeMap的第三个参数来限制并发内部订阅的数量。在所有请求完成后使用finalize 执行某些操作:

const concurrentRequests = 5;
from(objects)
    .pipe(
        mergeMap(obj => this.myService.updateObject(obj), concurrentRequests),
        tap(res => this.anotherService.set(res))),
        finalize(() => console.log('Sequence complete'))
    );

请参阅Stackblitz 上的示例。

【讨论】:

    【解决方案2】:
    from(objects).pipe(
      bufferCount(10),
      concatMap(objs => forkJoin(objs.map(obj => 
        this.myService.updateObject(obj).pipe(
          tap(value => this.anotherService.set(obj))
      )))),
      finalize(() => console.log('all requests are done'))
    )
    

    代码未经测试,但你明白了。如果需要任何错误或解释,请告诉我

    【讨论】:

    • 您的回答可能也会奏效,但我更喜欢 pschild 的方法。
    • 完全同意 - 不知道 mergeMapconcurrent 属性
    【解决方案3】:

    我曾经遇到过同样的问题。当我尝试从服务器加载多个图像时。我不得不一个接一个地发送http请求。我使用等待的承诺达到了预期的结果。下面是示例代码:

    async ngOnInit() {
        for (const number of this.numbers) {
          await new Promise(resolve => {
            this.http.get(`https://jsonplaceholder.typicode.com/todos/${number}`).subscribe(
              data => {
                this.responses.push(data);
                console.log(data);
                resolve();
              }
            );
          });
        }
      }
    

    这里的主要思想是在收到响应后解决承诺。 使用这种技术,您可以提出自定义逻辑,以便在所有请求完成后执行一个方法。

    这里是stackblitz。打开控制台以查看它的运行情况。 :)

    【讨论】:

    • 不再使用 Promises 是不好的做法。即使你有一个promise,也应该转换成一个Observable
    • 谢谢,但我想使用运算符来更好地理解它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-22
    • 1970-01-01
    • 1970-01-01
    • 2020-05-09
    • 1970-01-01
    • 2021-11-05
    • 1970-01-01
    相关资源
    最近更新 更多