3

To prevent writing a certain amount of code, such as this example:

    ...
    public getDataSet() : Observable<any>
    {
        return new Observable(observer => {
            if (this.dataset === undefined) {
                this.get().subscribe(data => {
                    this.dataset = new DataSet(data) ;
                    observer.next(this.dataset)
                    observer.complete() ;
                }) ;
            }
            else {
                observer.next(this.dataset)
                observer.complete() ;
            }
        }) ;
    }

I would like to use async/await feature but still return an Observable to remain consistent in using asynchronous data gathering services.

Thus according to RxJs's documentation, I should be using from() operator. https://www.learnrxjs.io/learn-rxjs/operators/creation/from

Here is what I try to implement (which looks much more concise) :

import { Observable, from } from 'rxjs' ;
...
        return from(async () => { // ✘ error
            if (this.dataset === undefined) this.dataset = new DataSet(await this.get().toPromise()) ;
            return this.dataset ;
        }) ;

However, TypeScript doesn't recognize async () => {} as an ObservableInput, but it does recognize this one :

import { Observable, from } from 'rxjs' ;
...
        return from(new Promise((resolve) => { // ✔ valid
            if (this.dataset === undefined) {
                this.get().toPromise().then(data => {
                    this.dataset = new DataSet(data) ;
                    resolve(this.dataset) ;
                }) ;
            }
            else resolve(this.dataset) ;
        })) ;

Though, JavaScript's async keyword makes a function always return a Promise.

console.log((async () => {}) ())
// Promise {}

Is there a way to make the from() RxJs operator accepting async promises ?

4 Answers 4

11

Async function returns a promise when called, before that it's just a function.

function fromAsyncFunction(): Observable<number> {
    return from((async () => {
      return 1;
    })()); // Call it
}
Sign up to request clarification or add additional context in comments.

2 Comments

Damn I just realized it when I received answers. I just needed to call it. Thanks !
It's certainly one of the most useful answer. I didn't now that.
2

What you're looking for is RxJS toPromise() function.

import { map } from 'rxjs/operators';

public getDataSet(): Promise<any> {
  return this.get().pipe(
    map(data => new DataSet(data))  // <-- transform incoming data from subscription
  ).toPromise();
}

You could then await this function.

// in some component

public async getDataSet() {
  this.dataSet = await this.someService.getDataSet();
}

But beware toPromise() is deprecated from RxJS v7 (current as of this writing) and would be gone in RxJS v8.

4 Comments

That's the other way around? OP is looking to return an Observable, not a Promise.
@RobertoZvjerković: I went through the question again and am still don't quite understand it. So OP wants to return an observable and use async/await on it?
I already was using toPromise(), but differently. The issue in your sample is that you don't check is this.dataset is already available. Your solution would work, but the async/await looks way better. Thanks for the proposition though !
@Tot: Sorry I overlooked the if check in the question. It looks like you're trying to have a caching mechanism for the value from the observable. You could use the shareReplay(1) with buffer 1 as shown by Poul Kruijt in his answer.
2

You can also do the following, because mixing promise with observable is frowned upon :):

public getDataSet() : Observable<any> {
  return this.dataset ? of(this.dataset) : this.get().pipe(
    map((dataset) => {
      this.dataset = new DataSet(data);
      return this.dataset;
    })
  );
}

However, this above solution might trigger double requests, if the first one hasn't finished yet.


It feels like you are trying to get it to cache the value. In that case you can do the following with the shareReplay() operator. This way the get request is triggered at the first subscribe, and after that the response is returned without triggering a request. It saves you the hassle of using an intermediary property:

private dataSet$ = this.get().pipe(
  map((data) => new DataSet(data)),
  shareReplay(1)
);

public getDataSet() : Observable<any> {
  return this.dataset$;
}

2 Comments

I think your solution is the best for my real case. However, it doesn't quite solve the original issue (to use async/await with RxJS's from()). I will accept another answer, but I hope yours will get the most upvotes !
@Tot Yep, it's a typical XY Problem
0

Why not just define your observable separately, and return it when you need it? shareReplay acts a cache for your dataset, so you shouldn't need to declare this.dataset at all.

dataset$ = get().pipe(
  map(data => new DataSet(data)),
  shareReplay(1)
);

public getDataSet() : Observable<any> {
  return this.dataset$.pipe(
    take(1)
  );
}

3 Comments

Same answer as above (from @Poul_Kruijt)... oh in fact better as it uses the DataSet type ! Though sometimes I'd like to use await ...toPromise(), which would not benefit the shareReplay(1) operator. I've just tested it and of course it completes the observable. Sometimes observables are very practicle and fluent, sometimes it's a mess compared to async/await. I'll have to see what's best.
async/await is syntactic sugar for promises. Since it has support in the language spec it is going to be nicer as some of the complexity is abstracted into the language itself. That being said, I would avoid mixing Observables and Promises except at the boundaries between libraries that use one or the other.
We're looking for the answer to the question. If you're willing to go this direction then just next a BehaviorSubject in the promise's .then, it would be simpler.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.