简体   繁体   English

RxJS 可观察的 forkJoin 未并行执行

[英]RxJS Observable forkJoin Not Executing in Parallel

I have the following code and, for the life of me, can't figure out why the requests don't execute concurrently.我有以下代码,对于我的一生,无法弄清楚为什么请求不会同时执行。 I'm still new to RxJS and observables, so any help in improving the code below would be greatly appreciated as well.我对 RxJS 和 observables 还是陌生的,因此对于改进以下代码的任何帮助也将不胜感激。 Basically, I'm making a call to a REST API on the backend to get some data.基本上,我在后端调用 REST API 以获取一些数据。 Then, for every element in that array of data I'm making another request to a different endpoint (hence using the 'forkJoin' operator).然后,对于该数据数组中的每个元素,我正在向不同的端点发出另一个请求(因此使用“forkJoin”运算符)。 All the requests get sent at once, but they seem to execute one after another still instead of concurrently.所有请求都一次发送,但它们似乎仍然一个接一个地执行,而不是同时执行。

this.sites$.subscribe(data => {

    // data.forEach(element => {
    //     this.siteCaptureMap[element.id] = new CaptureData();
            
    //     this.sitesService.getCaptureData(element.nameOrNumber, element.owner.name).subscribe(data => {
    //         this.siteCaptureMap[element.id].count = data.length;
    //     });
    // });

    var obs: Observable<any>[] = [];
    for (var _i = 0; _i < data.length; _i++) {
        this.siteCaptureMap[data[_i].id] = new CaptureData();
        this.siteCaptureMap[data[_i].id].id = _i;
        obs.push(this.sitesService.getCaptureData(data[_i].nameOrNumber, data[_i].owner.name));
    }

    forkJoin(obs).subscribe(results => {
        for (var _i = 0; _i < results.length; _i++) {
            this.siteCaptureMap[data[_i].id].count = results[_i].length;
        }
    });


    this.dataSource.data = data;
    this.dataSource.filteredData = data;
});

Again, any help would be greatly appreciated.再次,任何帮助将不胜感激。 If I need to clarify anything or provide any additional code snippets, please let me know!如果我需要澄清任何事情或提供任何其他代码片段,请告诉我! Thanks!谢谢!

Having nested subscribe s can lead to memory leaks and can be tough to unsubscribe so whenever you have nested subscribe s, think of switchMap , concatMap , and mergeMap .嵌套的subscribe可能会导致 memory 泄漏,并且很难取消订阅,因此每当您嵌套了subscribe时,请考虑switchMapconcatMapmergeMap

They all have small variations but they switch to a new observable from the previous observable.它们都有微小的变化,但它们从以前的 observable 切换到新的 observable。 This post explains the differences.这篇 文章解释了这些差异。

For you, I would try doing:对你来说,我会尝试做:

import { switchMap } from 'rxjs/operators';

...
this.sites$.pipe(
  switchMap(data => {
    let obs: Observable<any>[] = [];
    for (let _i = 0; _i < data.length; _i++) {
        this.siteCaptureMap[data[_i].id] = new CaptureData();
        this.siteCaptureMap[data[_i].id].id = _i;
        obs.push(this.sitesService.getCaptureData(data[_i].nameOrNumber, data[_i].owner.name));
    }

    return forkJoin(obs);
  }),
).subscribe(results => {
        for (let_i = 0; _i < results.length; _i++) {
            this.siteCaptureMap[data[_i].id].count = results[_i].length;
        }

        this.dataSource.data = data;
        this.dataSource.filteredData = data;
    });

A side note is to use let and const instead of var .附带说明是使用letconst而不是var

Also, if you see all the requests going out at the same time, that's all you can hope for.此外,如果您看到所有请求同时发出,这就是您所希望的。 If it returns in series, it can either be the browser or the server causing that.如果它连续返回,则可能是浏览器或服务器造成的。

First I would rearrange the code to be more rxjs idiomatic, removing nested subscriptions and using pipe and making it a bit more functional-style.首先,我将重新排列代码以使其更符合 rxjs 的习惯,删除嵌套订阅并使用pipe并使其更具功能风格。 Inline comments try to explain the changes内联注释试图解释变化

this.sites$.pipe(
  // this is the rxjs map operator that transform the data received from
  // the rest api into something different
  map(data => {
    // I create the obs array using the map method of JS arrays - this is 
    // NOT the rxjs map operator
    obs = data.map((element, _i) => {
       this.siteCaptureMap[element.id] = new CaptureData();
       this.siteCaptureMap[element.id].id = _i;
       return this.sitesService.getCaptureData(element.nameOrNumber, element.owner.name)
    });
    // return both the array of observables and the data
    return [data, obs];
  }),
  // concatMap makes sure that you wait for the completion of the rest api
  // call and the emission of the data fetched before you execute another
  // async operation via forkJoin
  concatMap(([data, obs]) => forkJoin(obs).pipe(
    // this is the rxjs map operator used to return not only the results
    // received as result of forkJoin, but also the data received with the
    // first rest call - I use this operator to avoid having to define 
    // a variable where to store 'data' and keep the logic stateless 
    map(results => ([data, results]))
  ))
).subscribe(([data, results]) => {
  // here I use forEach to loop through the results and perform
  // your logic
  results.forEach((res, _i) => this.siteCaptureMap[data[_i].id].count = res.length)
})

I think this form is more in line with rxjs and its functional spirit, and should be equivalent to your original form.我觉得这个表格更符合rxjs及其功能精神,应该和你原来的表格是等价的。

At the same time I would be interested to know why you say that your code executes the calls within forkJoin one at the time.同时,我很想知道您为什么说您的代码当时在forkJoin one 中执行调用。

By the way, if you are interested in looking at common patterns of use of rxjs with http, you can read this article .顺便说一句,如果您有兴趣查看 rxjs 和 http 的常见使用模式,您可以阅读这篇文章

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM