user11450057
user11450057

Reputation:

How can I change the order in subscribe?

How can I change the order of execution? I have an array this.friend, I need work with this array, but it takes to fill up with data

this.listUserService.getUsers().subscribe(
    async (data: any) => {
           this.listUsers = data;
           await data.forEach((current: any) => {
                 if (current.name === userSession) {
                      this.user = current;
                      this.friendsService.getFriends(this.user.id).subscribe(response => {
                                console.log('last', response);//this runs second
                                this.friends = response;
                            });
                        }
                    });
                    console.log('friends', this.friends); //friend is clear, 
this runs first
                    this.listUsers = this.listUsers.filter(user => !this.friends.some(relationship => relationship[0].friend.id !== user.id));
                }
            );

The console messages are shown in reverse order, how can I change the order, once it's full I want to use the Filter function

Upvotes: 0

Views: 1140

Answers (3)

Alexander Staroselsky
Alexander Staroselsky

Reputation: 38767

You could try instead using RxJS operators such as switchMap, map, and tap to control the flow of the observable including performing side effects like setting class properties.

This answer assumes that there is only a single current user resulting from if (current.name === userSession) { inside the forEach() and subsequently that this.friendsService.getFriends(user.id) only executes once.

import { map, switchMap, tap } from 'rxjs/operators'; // need to import operators

// ...

this.listUserService.getUsers().pipe(
  tap(users => this.listUsers = users), // side effect to set this.listUsers
  map(users => users.find(user => user.name === userSession)), // find current user
  switchMap(user => this.friendsService.getFriends(user.id)), // get friends of current user
  tap(friends => this.friends = friends), // side effect to set this.friends
  map(friends => this.listUsers.filter(user => this.friends.some(relationship => relationship[0].friend.id !== user.id)))
).subscribe(listUsers => {
  this.listUsers = listUsers;
});

You should definitely review the RxJS documentation for more information and examples of the various operators. Also you will definitely need to improve error handling in case situations like a current user is not found or any of the service/api calls fail.

Hopefully that helps!

Upvotes: 0

That's because you're using RxJS streams in the wrong way. You setup your stream first, so any other console.log would run first. The Rx stream is async by itself, so you don't need async await. The other console.log will run when the stream actually is called and receives data.

The RxJS way would be to .pipe() your filter() inside the Rx stream.

It's not so much an Angular problem, it's more RxJS related. Here's an example of what it could look like:

   this.listUserService
  .getUsers()
  .pipe(
    tap(listUserData => {
      this.listUsers = listUserData;
    }),
    switchMap(data => {
      return this.friendsService.getFriends(data.id).pipe(
        tap(friends => {
          this.friends = friends;
        })
      );
    })
  )
  .subscribe();

The switchMap switches to another observable underwater, so you don't need two subscriptions. This is considered good practice!

I would highly recommend spending some time learning RxJS, it'll make your experiences in Angular much more enjoyable and productive!

I can help you out with that if you'd like!

Upvotes: 0

wentjun
wentjun

Reputation: 42526

There is no need to mix promises with observables. In addition, it is not a good idea to nest subscribe() methods within each other. I would recommend you to handle the above operation with RxJS operators.

First, we use mergeMap to map over the observable values from the getUsers() method from the service, into an inner observable.

Then, we use forkJoin to wait the Array.forEach() loop to be completed before returning all the observables. Since you seem to be familiar with the usage of Promises in JavaScript, it is actually similar to Promise.all.

Last but not least, we handle the filtering of the listUsers at the subscribe() block. This will ensure that your asynchronous code is properly handle.

this.listUserService.getUsers().pipe(
  mergeMap(data => {
    this.listUsers = data;
    const observablesList = [];
    data.forEach((current: any) => {
      if (current.name === userSession) {
        observablesList.push(this.friendsService.getFriends(current.id));
      }
    });
    return forkJoin(observablesList);
  })
).subscribe(response => {
  // console.log(response) will give you the returned values from the looped getFriends()
  // handle the rest here

});

Upvotes: 1

Related Questions