Search code examples
javascriptrxjsgoogle-cloud-firestoreobservable

How to handle for promise inside a piped map


I am definitely sure I am confused here so please any help is appreciated.

Here is my scenario:

I pull from Firestore a document:

return this.afs.collection("events").doc(eventID).snapshotChanges().pipe(
      map( document => {

      })
    );

All is fine up to here.

But inside the map I need a promise to resolve (or not)

For example:

return this.afs.collection("events").doc(eventID).snapshotChanges().pipe(
      map( document => {
        // This is a promise the below part 
        const data = await EventImporterJSON.getFromJSON(document.payload.data())
        return data
      })
    );

I understand that the await cannot happen there. I am very confused how to solve this, perhaps I have not worked long enough with observables and rxjs.

In the end what I am trying to achieve is:

Get the document. Map and process it but inside the process, I need to handle a promise.

I don't want to return that promise to the caller though.

Does this make sense?

Or have I structured this completely wrong?


Solution

  • This is a typical use-case for mergeMap or concatMap:

    return this.afs.collection("events").doc(eventID).snapshotChanges().pipe(
      mergeMap(document => {
        // This is a promise the below part 
        return EventImporterJSON.getFromJSON(document.payload.data())
      })
    );
    

    However, you can also use async - await because operators such as mergeMap handle Observables, Promises, arrays, etc. the same way, so you can just return a Promise in mergeMaps project function it will work fine.

    Typically, you don't need to use multiple awaits in a single method because the more "Rx" way of doing things is chaining operators, but if you want, you can because the async method returns a Promise and RxJS will handle it like any other Promise.

    const delayedPromise = () => new Promise(resolve => {
      setTimeout(() => resolve(), 1000);
    })
    
    of('a').pipe(
      mergeMap(async v => {
        console.log(1);
        await delayedPromise();
        console.log(2);
        await delayedPromise();
        console.log(3);
        await delayedPromise();
        return v;
      })
    ).subscribe(console.log);
    // 1
    // 2
    // 3
    // a
    
    
    Live demo: https://stackblitz.com/edit/rxjs-3fujcs