WSD
WSD

Reputation: 3597

Observable's subscribe is never triggered

I'm facing an strange behavior while subscribing to my Observable. For some reason the console.log(item) is never called and also there are no errors on the console.

this.store$
.pipe(
  select(getRecentNotes),
  mergeAll(),
  groupBy(item => item.status.code),
  mergeMap(group => group.pipe(toArray())),
  toArray()
)
.subscribe((items)=> console.log('Grouped Items', items));

BTW, If I pipe only select(getRecentNotes) then it works and I'm getting something like this:

[
  {id: 1, note: "ABC", status: {code: 1, name: 'active'}},
  {id: 2, note: "DEF", status: {code: 1, name: 'active'}},
  {id: 3, note: "HIJ", status: {code: 2, name: 'hidden'}}
]

more interesting is that the following will work:

// This will work
this.store$
.pipe(
  select(getRecentNotes),
  mergeAll()
)
.subscribe((items)=> console.log('Bunch of Items', items));

But if I add another operator, like toArray() then it stops working.

// This will not work
this.store$
.pipe(
  select(getRecentNotes),
  mergeAll(),
  toArray()
)
.subscribe((items)=> console.log('All items', items));

Is there any hidden rule that I'm missing out? I've tried to recreate the problem at StackBlitz using just a simple Observable.from([]) but everything seems to works.

Update 1: I added some other operators to the pipe, they do stuff (like printing to the console). But the code in the .subscribe is not called

this.store$
.pipe(
  select(getRecentNotes),
  mergeAll(),
  tap((item)=> {console.log(item)}),   // <--- This is printed to the console
  reduce((acc, item)=> {
  console.log(acc);                    // <-- This is printed to the console
  console.log(item);
  return acc.push(item);
  },[]) 
)
.subscribe((items)=> console.log('All items', items));

Upvotes: 2

Views: 412

Answers (1)

Daniel Gimenez
Daniel Gimenez

Reputation: 20599

As @AnjilDhamala mentioned in his comment, operators like toArray don't execute until the stream completes. Your store$ observable stays open for changes so that toArray operator will never emit a result.

It seems like your goal is to return an array of arrays chunked by a code?

The simple solution would be to add take(1) after your select operator call. That should result in the last toArray returning an array of arrays.

But if my assumption is correct, then all of this may be unnecessary. It might seem that using all those rxjs operators is clean, but the intention of what you're trying to accomplish is far from clear. After the select you can just call map with a function that will chunk your array up with plain old JavaScript.

this.store$
.pipe(
  select(getRecentNotes),
  map(x => chunkBy(x, x => x.status.code))
).subscribe((items)=> console.log('All items', items));

function chunkBy<T>(ary: T[], chunkKeySelector: (item: T) => any) {
  const result: T[][] = [];
  const indexMap = new Map<any, number>();

  for (let value of ary) {
    const key = chunkKeySelector(value);
    (indexMap.has(key)) 
      ? result[indexMap.get(key)].push(value)
      : indexMap.set(key, result.push([value]) - 1);
  }
  return result;
}

Upvotes: 1

Related Questions