订阅嵌套的Observable

时间:2016-10-25 22:39:11

标签: angular typescript rxjs

我有一个应用程序,它会生成一个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());
  }
}

然后我会做类似itemsService.fetchItems().subscribe(items => console.log(items))的事情但最终发生的事情是我得到一个可观察数组(来自fetchItem的每个响应)。我还需要订阅每个内部可观察对象,以便实际触发fetchItem请求。

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

3 个答案:

答案 0 :(得分:3)

我会这样做:

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}和{{1}发出的单独值(截至目前,这是一个未记录的功能)。然后我使用mergeMap()在单独的请求中获取其内容,并将其结果合并到运算符链中。

此plnkr示例打印到控制台:

{"id": 3}

答案 1 :(得分:2)

您可能遇到的问题是您没有展开

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)); } }

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

flatMap

如果您不介意单独获取每个项目的响应,现在上述工作正常。如果您需要获取所有项目,那么您应该对所有内部值使用subprocess.run(),但您仍然需要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); }); } 才能展平结果内部值:

    Public Function TrimStr(str As String, charsToRemove As String)
        If str.EndsWith(charsToRemove) Then
            Return str.Substring(0, str.Length - charsToRemove.Length)
        Else
            Return str
        End If
    End Function

答案 2 :(得分:-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的投影,我在上面的代码中进行了更改