Search code examples
angularcachingpromiseobservableionic5

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?

  • timeline-service.ts
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;
    }
  • timeline-page.ts
this.TimelineService.getTimelinePosts(limit, offset, type, group).subscribe(
            (data) => {
                this.setTimelineData(data);
            }
        );

Solution

  • 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));
            })
        );
    }