Angular 2 calling multiple asynchronous methods

I have a mobile application that I am creating and am currently working on authentication. Before I get to my homepage, I need to use many endpoints in the API that I created before I can display the data to the user.

All endpoints return the correct data when testing in Postman, however I get null in the second asynchronous call when I use it in my application.

I am sure this has something to do with the order in which these calls are made, so I was just looking for some help on how I can wait for one call to finish before starting another.

 public login() { this.showLoading(); this.userService.getUserIdFromUserName(this.registerCredentials.username) // WORKS .subscribe( res => { console.log(res); localStorage.setItem("UserId", res.toString()); }, err => { console.log(err); }); this.userService.getEmployeeIdFromUserId(localStorage.getItem("UserId")) // THIS RETURNS NULL .subscribe( res => { console.log(res); localStorage.setItem("EmployeeId", res.toString()); }, err => { console.log(err); }); this.authService.login(this.registerCredentials) .subscribe( data => { this.loading.dismissAll(); console.log('User logged in successfully! ', data); this.nav.push(TabsPage); localStorage.setItem("Username", this.registerCredentials.username); localStorage.setItem("isLoggedIn", "true"); }, error => { this.loading.dismissAll(); this.showAlert("Uh oh!", "Something went wrong. Please re-enter your login credentials or check your connection."); console.log(error); }); } 
+7
javascript angular typescript rxjs
source share
2 answers

There is an error in the source code that leads to this error. You have three calls in your code, which I will call A), B) and C):

 A) this.userService.getUserIdFromUserName(this.registerCredentials.username) // WORKS B) this.userService.getEmployeeIdFromUserId(localStorage.getItem("UserId")) // THIS RETURNS NULL C) this.authService.login(this.registerCredentials) 

What you need to know about RXJS is the difference between a cold Observable (which represents all the information needed to start an async operation) and a hot Observable (which an Observable with an asynchronous operation is already running).

The three calls A), B) and C) simply build cold observables that start from the moment you call .subscribe() on them. Thus, at time point B), A) is already running, but not yet completed. Thus, calling localStorage.getItem("UserId") will return null since A) has not yet called the next caller back.

So what do you want to do for B) to wait for A). In addition, instead of stuffing something into a global state (localStorage), it is probably better to pass the result from A) to B). Enter .mergeMap() :

 this.userService.getUserIdFromUserName(this.registerCredentials.username) // WORKS .map(res => res.toString()) .do(userId => localStorage.setItem("UserId", userId)) // cleanly separate side-effects into .do() calls .mergeMap(userId => this.userService.getEmployeeIdFromUserId(userId)) .map(res => res.toString()) .do(employeeId => localStorage.setItem("EmployeeId", employeeId)) .subscribe( employeeId => { console.log(employeeId); }, err => { console.log(err); }); 

The good thing about rxjs is that error handling works entirely through the Observable chain. If you can execute C) in parallel, see .forkJoin() .

Finally, if you need .mergeMap() explanations, look at this answer: SwitchMap vs MergeMap in the #ngrx example

+14
source share

That should work. Do not forget import 'rxjs/Rx'

 this.userService.getUserIdFromUserName(this.registerCredentials.username) .map(res => res.toString()) .do(userId => { console.log(res); localStorage.setItem("UserId", userId); }) .flatMap(userId => { return this.userService.getEmployeeIdFromUserId(userId); }) .do(res => { console.log(res); localStorage.setItem("EmployeeId", res.toString()); }) 
0
source share

All Articles