Maarten Heideman
Maarten Heideman

Reputation: 595

How wait for a async function in an observable?

I'll have an angular service with a function getTimeLinePosts to get JSON from a server. In the timeline-page.ts component I'll subscribe to that function. When sending a request to the server I'll need to send the date of the previous cached response (using ionic cache) to check if there is new data. The problem is the const date returns a promise instead of a value. I'll can't use async getTimeLinePosts because of the subscribe in de timeline-page.ts. Suggestions how I'll can wait for the date before returning the loadFromDelayedObservable?

getTimelinePosts(
    limit: number = 25,
    offset: number = 0,
    type: string = "",
    group: number = null
) {
    let cacheKey = `api/timeline?l=${limit}&o=${offset}&t=${type}&g=${group}`;

    // How wait for this value before fire request
    const date = this.getCachedItem(cacheKey);

    let request = this.http.post(cacheKey, { headers: HEADER.headers , params: JSON.stringify({ dateUpdated: date }) } );
    let groupKey = "timeline";
    let ttl = 60 * 60 * 1;
    let delayType = "none";

    return this.cache
        .loadFromDelayedObservable(url, request, groupKey, ttl, delayType)
        .pipe(map((result) => result));

}

    async getCachedItem(cacheKey){
        let data = await this.cache.getItem(cacheKey)
        .catch(() => {
            console.log("Oh no! My promise is expired or doesn't exist!");
        });
        return await data.data.dateUpdated;
    }
this.TimelineService.getTimelinePosts(limit, offset, type, group).subscribe(
            (data) => {
                this.setTimelineData(data);
            }
        );

Upvotes: 0

Views: 242

Answers (2)

Barremian
Barremian

Reputation: 31105

This looks like a typical case of combining observables and promises. I'd stick with converting the promise to an observable so we can leverage many of it's advantages.

You could use RxJS from function to convert the promise to an observable. Then you could use a higher order mapping operator like switchMap to map from one observable to another.

Try the following

import { from } from 'rxjs';
import { switchMap } from 'rxjs/operators';

getTimelinePosts(
    limit: number = 25,
    offset: number = 0,
    type: string = "",
    group: number = null
) {
    let cacheKey = `api/timeline?l=${limit}&o=${offset}&t=${type}&g=${group}`;

    return from(this.getCachedItem(cacheKey)).pipe(
        switchMap(date => {
            let request = this.http.post(cacheKey, { headers: HEADER.headers, params: JSON.stringify({ dateUpdated: date }) });
            let groupKey = "timeline";
            let ttl = 60 * 60 * 1;
            let delayType = "none";

            return this.cache
                .loadFromDelayedObservable(url, request, groupKey, ttl, delayType)
                .pipe(map((result) => result));
        })
    );
}

Upvotes: 1

Moshezauros
Moshezauros

Reputation: 2593

You can use observables - specifically mergeMap/flatMap, this is similar to sending a two requests one after the other, where the second one depends on the first:

getTimelinePosts(
    limit: number = 25,
    offset: number = 0,
    type: string = "",
    group: number = null
) {
    let cacheKey = `api/timeline?l=${limit}&o=${offset}&t=${type}&g=${group}`;

    this.getCachedItem(cacheKey)
        .pipe(mergeMap((date) => {
            let request = this.http.post(cacheKey, { headers: HEADER.headers , params: JSON.stringify({ dateUpdated: date }) } );
            let groupKey = "timeline";
            let ttl = 60 * 60 * 1;
            let delayType = "none";
        
            return this.cache
                .loadFromDelayedObservable(url, request, groupKey, ttl, delayType)
                .pipe(map((result) => result));
        }));
}

Upvotes: 0

Related Questions