11

Any suggestions, how this can be rewritten in more promise-chaining style?:

this.apiService.sendPutRequest('/api/users/activate', usrObj).pipe(
        map(() => {
            return this.apiService.sendGetRequest('/api/users/' + this.currentUserId).pipe(
                map(data => {
                    return this.setActiveUser(data).pipe(
                        map(() => {
                            return this.apiService.sendGetRequest('api/tasks/user/' + this.currentUserId).pipe(
                                map(tasks => {
                                    return this.taskService.setCurrentUserTasks(tasks);
                                })
                            );
                        })
                    );
                })
            );
        })
    );
askona
  • 390
  • 1
  • 5
  • 16

3 Answers3

13

You can use switchMap for handling observables and tap for side efects handling. And you need to subscribe because it's cold observable

For error handling use catchError for all requests

this.apiService.sendPutRequest('/api/users/activate', usrObj).pipe(
    catchError(err=> this.errorHandler(err)),
    switchMap(() => this.apiService.sendGetRequest('/api/users/' + this.currentUserId)
        .pipe(catchError(err=> this.errorHandler(err)))
    ),
    tap(data => this.setActiveUser(data)),
    switchMap(() => this.apiService.sendGetRequest('api/tasks/user/' + this.currentUserId)
        .pipe(catchError(err=> this.errorHandler(err)))
    ),
    tap(tasks => this.taskService.setCurrentUserTasks(tasks))
).subscribe()
Kliment Ru
  • 2,057
  • 13
  • 16
  • 1
    Why we need catchError() for each switchMap. why can't we have one? – Suresh Kumar Ariya Oct 12 '18 at 07:49
  • 2
    with one catchError when crush one observable whole stream will crushed. In this solution you can get partial data when one observable crushed. For example when `this.apiService.sendGetRequest('/api/users/' + this.currentUserId)` return error stream will continue and you can get `this.apiService.sendGetRequest('api/tasks/user/' + this.currentUserId)` – Kliment Ru Oct 12 '18 at 08:13
  • If you don't need partial data use one catchError – Kliment Ru Oct 12 '18 at 08:15
  • There is no need of nested pipes with catch error. Those can be piped in the main pipeline – Antoniossss Sep 14 '21 at 07:21
12

use SwitchMap for that.

mainApiCall.pipe(
    switchMap(result=>secondApiCall(result)),
    switchMap(resultFromSecondApiCall=>thirdApiCall(resultFromSecond))
...
and so on
)
Antoniossss
  • 31,590
  • 6
  • 57
  • 99
  • 2
    The naming choices in this code sample are descriptive and helpful for me to quickly understand chaining together. This got me where I needed to be with chaining various api calls together quickly. Cheers! – sofly Sep 02 '21 at 23:57
8

Use a single pipe for your problem. Just comma seperate different chainable operators like map, switchMap, mergeMap, tap, etc.

this.apiService.sendPutRequest('/api/users/activate', usrObj).pipe(
  switchMap((results) => this.apiService.sendGetRequest('/api/users/' + this.currentUserId)),
  tap((results) => this.setActiveUser(data)),
  switchMap(() => this.apiService.sendGetRequest('api/tasks/user/' + this.currentUserId)),
  tap((results) => this.taskService.setCurrentUserTasks(tasks))
);

Simplified: Use map if you just want to transform a value without any async api calls and pass it to another operator or a subscription, tap if you just want to catch values in between without transforming (e.g. for logging). And switchMap for dispatching additional api calls.

Felix Lemke
  • 6,189
  • 3
  • 40
  • 67