首页 文章

订阅嵌套的Observable

提问于
浏览
5

我有一个应用程序,它发出一个http请求来获取项目列表,然后为列表中的每个项目发出一个http请求,以获取有关每个项目的更多详细信息 . 有效:

class ItemsService {
  fetchItems() {
    return this.http.get(url)
    .map(res => res.json())
    .map(items => items.map(this.fetchItem(item)));
  }

  fetchItem(item: Item) {
    this.http.get(`${url}/${item.id}`)
    .map(res => res.json());
  }
}

然后我会做像_1383009这样的事情,但最终发生的事情是我得到一个可观察数组(每个响应来自 fetchItem ) . 我还需要订阅每个内部observable,以便实际触发 fetchItem 请求 .

我也尝试使用 flatMap 而不是map,但在这种情况下似乎有相同的结果 . 是否有任何方法可以订阅嵌套的observable?

3 回答

  • 2

    我会像下面这样做:

    function mockRequest() {
        return Observable.of('[{"id": 1}, {"id": 2}, {"id": 3}]');
    }
    function otherMockRequest(id) {
        return Observable.of(`{"id":${id}, "desc": "description ${id}"}`);
    }
    
    class ItemsService {
        fetchItems() {
            return mockRequest()
                .map(res => JSON.parse(res))
                .concatAll()
                .mergeMap(item => this.fetchItem(item));
        }
    
        fetchItem(item: Item) {
            return otherMockRequest(item.id)
                .map(res => JSON.parse(res));
        }
    }
    
    let service = new ItemsService();
    service.fetchItems().subscribe(val => console.log(val));
    

    观看现场演示:http://plnkr.co/edit/LPXfqxVsI6Ja2J7RpDYl?p=preview

    我正在使用.concatAll()的技巧将一个对象数组(如 [{"id": 1}, {"id": 2}, {"id": 3}] )转换为逐个发出的单独值 {"id": 1}{"id": 2}{"id": 3} (截至目前它是一个未记录的功能) . 然后我使用mergeMap()在单独的请求中获取其内容并将其结果合并到运算符链中 .

    这个plnkr示例打印到控制台:

    { id: 1, desc: 'description 1' }
    { id: 2, desc: 'description 2' }
    { id: 3, desc: 'description 3' }
    
  • 3

    您可能遇到的问题是您没有足够扁平 .

    flatMapmergeMap 将展平 ObservablesPromisesArrays ,甚至 generators (不要在最后一个引用我),几乎任何你想扔的东西 .

    所以当你做 .flatMap(items => items.map(item => this.fetchItem(item)) 时,你真的只是在做 Observable<Array<Item>> => Observable<Observable<Item>>

    当你做 map 时,你正在做 Observable<Array<Item>> => Observable<Array<Observable<Item>>> .

    您需要做的是首先展开数组,然后展平每个请求:

    class ItemsService {
      fetchItems() {
        return this.http.get(url)
        .map(res => res.json())
        // Implicitly map Array into Observable and flatten it
        .flatMap(items => items)
        // Flatten the response from each item
        .flatMap((item: Item) => this.fetchItem(item));
      }
    }
    

    现在,如果您不介意单独获取每个项目响应,则上述工作正常 . 如果你需要获得所有项目,那么你应该在所有内部值上使用forkJoin,但是为了展平得到的内部值,你仍然需要 flatMap

    fetchItems(): Observable<Response[]> {
      return this.http.get(url)
        .map(res => res.json())
        .flatMap(items => {
           const requests = items.map(item => this.fetchItem(item));
           return Rx.Observable.forkJoin(requests);
        });
    }
    
  • -1

    您可以在调用 this.fetchItem 的行之前拆分items数组 . 您可以在Observable上使用mergeMap,其值是一个数组,每个项目将单独发出 .

    fetchItems() {
        return this.http.get(url)
           .map(res => res.json())
           .mergeMap(arrItem => this.fetchItem(arrItem));
    }
    

    编辑:我想我应该提供更多解释 . mergeMap 与rxjs中的 flatMap 同义 . 通常,当你的投影函数返回一个Observable时你使用flatMap,但它也会使数组变平,所以,调用mergeMap然后将单独发出每个项目,我认为这是OP想要实现的 . 我也意识到,你可以组合 mergeMap 调用和最后 map 调用,因为将为数组中的每个项调用 mergeMap 的投影,我在上面的代码中进行了更改 .

相关问题