hardywang
hardywang

Reputation: 5162

RxJS subscribe and local variable is null issue

I have written this function in my Angular application to call a RESTful service, the class is outside a Component as a service class

getProducts(): IProduct[] {
    let productsObservable = this.http.get<IProduct[]>(this.productUrl).pipe(
        tap(data => console.log("All: " + JSON.stringify(data))),
        catchError(this.serviceErrorHandler)
    );

    let products: IProduct[];
    let errorMessage: string = "";

    productsObservable.subscribe(
        p => {
            products = p;
            console.log("All in subscribe: " + JSON.stringify(products));
        },
        error => errorMessage = <any>error
    );

    if (errorMessage.length > 0) {
        console.log(errorMessage);
        throw new Error(errorMessage);
    }

    console.log("All before return: " + JSON.stringify(products));
    return products;
}

I have 3 lines of code with console.log to track my data.

If the consumer of Http Observable is in the same service class, the result is null. If I break the consumer of service request into Angular's component class, it works fine. What makes the Rx's Observable so strange? I think I soon as I subscribe to it, it will be executed.

Any idea what is wrong with my code?

Upvotes: 0

Views: 1720

Answers (1)

ggradnig
ggradnig

Reputation: 14149

Yup, this happens because the function passed to subscribe is not invoked before you return the products list.

Consider a simpler example:

function f() {
    let flag: boolean = false;

    setTimeout(() => flag = true, 10);

    return flag;
}

With setTimeout, you put a new task on the JavaScript Event Loop, just like with http.get. Tasks on the event loop are always executed after the function that put them there returns. In my example, the return value will always be false, no matter how low the timeout is.

Coming back to your example, what you need is to return an Observable from your function. Instead of trying to assign the return value of the GET call to a local products variable, you directly return the GET call. Like this:

getProducts(): Observable<IProduct[]> {
    // your get call ...
    return productsObservable;
}

Now, this will probably not solve everything for you, because you now need to deal with the Observable in your consumer functions. But that's more or less the point of the whole Observable pattern.. to notify the end-consumer of data changes.

In Angular, you can deal with Observables using the async pipe.

Upvotes: 1

Related Questions