adrisons
adrisons

Reputation: 3723

Rxjs switchMap + toArray

I am having a problem with rxjs.

I have a function that should do this:

When execution reaches toArray nothing happens, no result.

Code

  get chats$(): Observable<Chat[]> {
    return of(['1', '2']).pipe(
      filter(groupIds => !!groupIds && groupIds.length > 0),
      switchMap(groupIds => groupIds),
      switchMap(groupId => getGroupChats(groupId)), // fetch list of chats for the group id
      toArray(),
      map(doubleList => {
        return ([] as Chat[]).concat(...doubleList); // merge chat lists
      })
    );
  }

I have tried this too:

get chats$(): Observable<Chat[]> {
    return of(['1', '2']).pipe(
      filter(groupIds => !!groupIds && groupIds.length > 0),
      map(groupIds =>
        groupIds.map(groupId => getGroupChats(groupId))
      ),
      switchMap(chatList$ =>
        forkJoin(chatList$).pipe(
          map(doubleList => {
            return ([] as Chat[]).concat(...doubleList);
          })
        )
      )
    );
}

Test

Test response is: Error: Timeout - Async callback was not invoked within 5000ms

describe("WHEN: get chats$", () => {
  const CHAT_MOCK_1: Chat = {
    id: "1",
  };
  const CHAT_MOCK_2: Chat = {
    id: "2",
  };

  it("THEN: get chats$ should return chat list", (done) => {
    service.chats$
      .subscribe((data) => {
        expect(data.length).toEqual(2);
        expect(data[0]).toEqual(CHAT_MOCK_1);
        expect(data[1]).toEqual(CHAT_MOCK_2);
        done();
      })
      .unsubscribe();
  });
});

Upvotes: 0

Views: 852

Answers (2)

adrisons
adrisons

Reputation: 3723

Finally this is what I have done (and it works):

  • use plain Array.map to transform our group ids array to a list of observables, each observable containing the array of chats for that group.
  • use forkJoin to get final emitted value of each observable of the generated Array.

Code

get chats$(): Observable<Chat[]> {
    return this.groupsIds$.pipe(
        skipUntil(this._groupsLoaded$),
        switchMap((ids) => {
            const chatsList: Observable<Chat[]>[] = ids.map((id) =>
                this.getGroupChats$(id)
            );

            return forkJoin([...chatsList]).pipe(
                map((list) => ([] as Chat[]).concat(...list))
            );
        })
    )
}

I still have some doubts about why this works and not the previous versions, if someone could explain that would be great.

As a conclusion: do not concat multiple switchMap.

Upvotes: 0

Ajantha Bandara
Ajantha Bandara

Reputation: 1521

This code snippet will take an array of ids, fetch result separately and collect to a array

from([1,2,3,4])
  .pipe(
    mergeMap(a => of(a * 10)), // send request if you need hare or any observable
    toArray()
  ).subscribe(console.log);

Upvotes: 1

Related Questions