CodeCode123
CodeCode123

Reputation: 47

How to rewrite this code using rxjs observables instead async/await?

I need that createUser function returns Observable<UserEntity> but in this function I also have to make 2 queries to DB and check if this user exists. The code below uses async/await and looks pretty good and clean. But the problem is that I use rxjs everywhere in this project and would like to write it somehow using rxjs. Can it be as clean as now but with Observables?

async create(user: CreateUserDTO): Promise<UserEntity> {
    const userByEmail = await this.getUserByEmail(); 

    const userByLogin = await this.getUserByLogin(); 
    if (userByLogin || userByEmail)
        // retrun error here

    return await this.createUser(user);
}

Upvotes: 0

Views: 691

Answers (2)

Saptarsi
Saptarsi

Reputation: 888

I am using RxJs 6.5

forkJoin will emit result when both async functions getUserByEmail & getUserByLogin complete their execution

If getUserByEmail & getUserByLogin returns Promise,for that using from to convert a promise into an observable

mergeMap to subscribe the inner observable.In our case createUser returns observable

create(user: CreateUserDTO): Observable < UserEntity > {

    //If getUserByEmail & getUserByLogin returs Promise

    const getUserByEmail$ = from(this.getUserByEmail());
    const getUserByLogin$ = from(this.getUserByLogin());

    //If Both returns Observable
    //const getUserByEmail$ = this.getUserByEmail();
    //const getUserByLogin$ = this.getUserByLogin();


    return forkJoin({
        userByEmail: this.getUserByEmail(),
        userByLogin: this.getUserByLogin(),
    }).pipe(
        tap((res) => {
            if (res.userByEmail || res.userByLogin) {
                throw 'User exists!';
            }
        }),
        mergeMap(() => {
            return from(this.createUser(user));
            //If createUser returns Observable,then
            //return this.createUser(user);
        })
    );
}

Upvotes: 2

Picci
Picci

Reputation: 17762

Assuming that this.getUserByEmail(), this.getUserByLogin() and this.createUser(user) return Promises, the code could look like this

create(user: CreateUserDTO): Observable<UserEntity> {
    // with the rxjs from function we turn a Promise into an Observable
    const userByEmail$ = from(this.getUserByEmail()); 
    const userByLogin$ = from(this.getUserByLogin()); 

    // with forkjoin we create an Observable which notifies when all the 
    // Observables which have been passed in as parameters notify
    return forkJoin([userByEmail$, userByLogin$]).pipe(
       // with concatMap you wait for the upstream Observable (i.e. the 
       // Observable created by forkJoin) to notify and complete, and then
       // you return the next Observable in the chain, which is, in this case,
       // the Observable which (when subscribed) creates the user
       concatMap(([userByLogin, userByEmail]) => 
          if (userByLogin || userByEmail) {
              // throw error here
          }
          return from(this.createUser(user))
       })
    )
}

Otherwise, if this.getUserByEmail(), this.getUserByLogin() and this.createUser(user) return Observables you do not need to use the from rxjs function and the code would be slightly simpler, like this

create(user: CreateUserDTO): Observable<UserEntity> {
    return forkJoin([this.getUserByEmail(), this.getUserByLogin()]).pipe(
       concatMap(([userByLogin, userByEmail]) => 
          if (userByLogin || userByEmail) {
              // throw error here
          }
          return from(this.createUser(user))
       })
    )
}

Upvotes: 2

Related Questions