1
votes

I have a mobile app, built with ionic and angular and I want to upgrade that from rxjs 5.5 to rxjs 6. However it appears that there are some breaking changes in the way rxjs uses operators, especially in the way values returned by one switchMap-operator are handled in the next operator in the pipe-chain. This is not clear from the migration docs (https://rxjs-dev.firebaseapp.com/guide/v6/migration), and I cannot find a way to work around it.

The problem might be in my use of function bodies within the switchMap-operators. That approach was based on older Internet-sources, but I see it less in more recent examples, and not sure if it is still supposed to be used.

The reason I need that approach is that the way I use observables is more or less as a replacement for promises - it's a way to make sure that different steps run in sequence. So there is not a single data-stream that runs through the operators, but any operator might be the start of a new type of event. That was a decision made in the past, in order to prevent constant conversions between observables and promises. Looking at it now, it might not be good practice, but for now, I would prefer to leave as is, if possible.

Anyway, take the following as an example:

initializeStatisticsObs(): Observable<any> {
    return this.speciesService.getImagesSizeInBytesObs()
      .pipe(
        switchMap((fSizeInBytes: number) => {
          this.imagesSizeInBytes = fSizeInBytes;
          return this.speciesService.getImagesNumberOfItemsObs();
        }),
        switchMap((fNumberOfItems: number) => {
          this.imagesNumberOfItems = fNumberOfItems;
          return of({});
        }),
..
      )
  }

  getImagesSizeInBytesObs(): Observable<number> {
    return this.speciesLocalStorageService.getSizeInBytesObs();
  }

  getImagesNumberOfItemsObs(): Observable<number> {
    return this.speciesLocalStorageService.getNumberOfItemsObs();
  }

As you can see, the higher level function initializeStatisticsObs() calls two other functions, that both return a number, wrapped in an Observable. Both numbers should ultimately be stored in variables of type number. I know that in this case the whole idea of running asynchronously doesn't really seem to make sense, but it is just a simplified example.

So the call to this.speciesService.getImagesSizeInBytesObs() is called outside a pipe-chain, its output is picked up in the first switchMap, and stored in a variable of type number.

Then a call is made to a second, similar function, this.speciesService.getImagesNumberOfItemsObs(), which again returns a number wrapped in an Observable. This second call should be picked up by the second switchMap operator and again stored in a variable of type number.

This approach worked perfectly well in rxjs 5.5, but I get build-errors in version 6.4.0 or higher. The issue is that apparently the switchMap-operator now adds an additional Observable wrapper, so the second switchMap gets as input a value of type <Observable<number>>, conflicting with the number type of this.imagesNumberOfItems.

The build error I get looks like this:

    Argument of type 'OperatorFunction<number, Observable<{}>>' is not assignable to parameter of type
    'OperatorFunction<Observable<number>, Observable<{}>>'. Type 'number' is not assignable to type
    'Observable<number>'.

The reason why I believe the use of function bodies is the cause of the problem is that when I use this:

 initializeStatisticsObs(): Observable<any> {
    return this.speciesService.getImagesSizeInBytesObs()
      .pipe(
        switchMap((fSizeInBytes: number) => of(23)),
        switchMap((fNumberOfItems: number) => {
          this.imagesNumberOfItems = fNumberOfItems;
          return of({});
        })
 ..
      )
  }

it works fine.

And by the way, the tap() operator can be used with function bodies without these problems, but they aren't suitable for my purposes. Is there a way to resolve this?

1

1 Answers

0
votes
switchMap((fSizeInBytes: number) => {
      this.imagesSizeInBytes = fSizeInBytes;
      return this.speciesService.getImagesNumberOfItemsObs();
    })

switchMap return Observable and getImagesNumbersOfItemsObs return Observable so your return is Observable>

you should use tap here

tap((fSizeInBytes: number) => this.imagesSizeInBytes = fSizeInBytes)
switchMap(() => this.speciesService.getImagesNumberOfItemsObs())
tap((fNumberOfItems: number) => this.imagesNumberOfItems = fNumberOfItems))

but better to use forkJoin operator here and expected your values as array in subscribe