简体   繁体   English

Angular / RxJS:带有可观察项的嵌套服务调用

[英]Angular/ RxJS: nested service calls with observables

I'm new to RxJS observables and I'm trying to resolve a rather simple use case. 我是RxJS可观察对象的新手,正在尝试解决一个相当简单的用例。

In a service, I first make a http call that returns an item (as an observable). 在服务中,我首先进行http调用,以返回一个项目(作为可观察的对象)。 The item contains an array of ids, some of them repeated. 该项目包含ID数组,其中一些重复。 For each distinct id, I need to call another http service (again returns an observable), and add its return value to the original item in place of the corresponding id. 对于每个不同的ID,我需要调用另一个http服务(再次返回一个observable),并将其返回值添加到原始项目中,以代替相应的ID。 These calls should happen in parallel. 这些调用应并行进行。 Finally, once every call has completed, my service should return an observable of the original item, now with its sub-items in place. 最后,每次通话结束后,我的服务应返回可观察到的原始项目,现在已包含其子项目。

To give a better idea, this is what it would look like with promises rather than observables: 为了给出更好的主意,这是用promise而不是可观察的东西看起来的样子:

MyService() {
    return HttpGetMainItem()
        .then(item => {
            var promises = _.uniq(item.subItems)
                 .map(sid => HttpGetSubItem(sid)
                            .then(subItem => {
                                // add to matching item.subItems
                             }));
            // wait for all promises to complete and return main item
            return Promise.all(promises).then(() => item);
        });
}

What would be the best way to accomplish this working with observables? 用可观察物完成此工作的最佳方法是什么?

EDIT: from the answers it seems I wasn't very clear. 编辑:从答案看来我不是很清楚。 The example with promises is just for clarity, in my case the http calls are actually Angular's HttpClient.get, so they return observables- I'm looking to do everything with observables. 带有promises的示例只是为了清楚起见,在我的情况下,http调用实际上是Angular的HttpClient.get,因此它们返回的是observables-我正在寻找使用observables做的所有事情。

Here's one way you can accomplish the above with rxjs. 这是使用rxjs完成上述操作的一种方法。

  1. Convert the promise to an observable using from . 使用from将promise转换为可观察的。
  2. In the outer pipe call switchMap so that you can call another observable and return that as the result of the outer observable. 在外部管道中,调用switchMap以便您可以调用另一个可观察对象,并将其作为外部可观察对象的结果返回。 You're switching execution contexts. 您正在切换执行上下文。
  3. Inside the switchMap do the map of subItems as you did before and then use forkJoin to create an Observable of all the elements' promise. 在switchMap内,像以前一样执行子项的映射,然后使用forkJoin创建所有元素的诺言的Observable。 ForkJoin will emit an array of all the results once all promise complete. 一旦所有承诺都完成,ForkJoin将发出所有结果的数组。 It's like promise.all . 就像promise.all
  4. Add the items like you were planning to do before, and return the original item. 像以前一样计划添加项目,然后返回原始项目。

Code

from(HttpGetMainItem()).pipe(
    switchMap(item => 
        forkJoin(_.uniq(item.subItems).map(sid => HttpGetSubItem(sid)))
            .pipe(
                map(results => { 
                    /* add results to matching items.subItems and */
                    return item;
                })
            )
     )
);

I feel this looks a bit clunky, due to the need to retain the root item and the nesting it requires. 我觉得这有点笨拙,因为需要保留根项目及其所需的嵌套。 You can use the selector parameter of switchMap to combine the outer and inner observable. 您可以使用switchMap的选择器参数来组合外部和内部可观察对象。 You can use that parameter in place of the logic you had in map() and since both observables' results are passed you won't need any further nesting. 您可以使用该参数来代替您在map()使用的逻辑,并且由于两个可观察对象的结果都已传递,因此您不需要任何进一步的嵌套。

from(HttpGetMainItem()).pipe(
    switchMap(
        item => forkJoin(_.uniq(item.subItems).map(sid => HttpGetSubItem(sid))),
        (item, results) => { 
             /* add results to matching items.subItems and */
             return item;
        }
    )
);

I believe you need something like this: 我相信您需要这样的东西:

import { from, forkJoin } from 'rxjs';
import { switchMap } from 'rxjs/operators';

itemWithSubItems$ = from(HttpGetMainItem()).pipe(
    switchMap(item => {
        const promises = _.uniq(item.subItems).map(item => HttpGetSubItem(item.sid));
        return forkJoin(promises)
            .map(resultArray => item.subItems = resultArray)
            .map(_ => item);
    })
);

First fetches the main item. 首先获取主要项目。 Then use forkJoin to resolve all subqueries and enrich the main item. 然后使用forkJoin解决所有子查询并充实主要项目。 After that just return the main item. 之后,只需返回主要项目即可。

Maybe you could use a lib like async.each -> https://caolan.github.io/async/docs.html#each (eachSeries maybe). 也许您可以使用像async.each-> https://caolan.github.io/async/docs.html#each (可能是eachSeries)之类的库。

would be something like : 就像这样:

async.each(array, (obj, cb) => {
  observable with obj in parameter and with subscriber result : 
  cb(err, subscriberRes);
}, (err, res) => {
  console.log(res);
}

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

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