0

I'm trying to populate my recipeList in my RecipesComponent constuctor but my then() block is being executed before my async GetRecipesService.getRecipes() has finished.

export class RecipesComponent{
    constructor(getRecipesService: GetRecipesService){
        getRecipesService.getRecipes().then(promise=>{
            this.recipeList = getRecipesService.recipes;
            console.log("RecipesComponent recipeList: "+this.recipeList);
        });
        
    }
    recipeList = [];

}

@NgModule()
export class GetRecipesService{
    recipes: any[];
    constructor(private http:HttpClient){
       this.http = http;
    }
    async getRecipes(){
        this.http.get<any[]>('http://localhost:4200/recipes').subscribe(response=>{
            this.recipes = response;
            console.log("getRecipes this.recipes: "+this.recipes);
           
        })
    }
}

In my web browser my console output is:

  1. RecipesComponent recipeList: undefined

  2. getRecipes this.recipes: [object Object],....[object Object]

How do I get my RecipesComponent to wait for getRecipes to finish?

4
  • getRecipes is not returning anything Commented Jul 29, 2020 at 0:55
  • getRecipes populates recipes: any[] Commented Jul 29, 2020 at 0:57
  • You can only awaits a promise, the http client returns observables. We don't use promises in Angular, it is built upon RxJs. If you are not familiar with RxJs then it is time to pause you Angular journey and learn RxJs. Make sure you have a reasonable understanding of observables before continuing on your Angular journey. Commented Jul 29, 2020 at 1:00
  • @AdrianBrand Thank you so much for the input. I'm looking into RxJs observables right now. In the mean time. Do I need to majorly change the structure of my code to get this implementation working? Commented Jul 29, 2020 at 1:04

3 Answers 3

1

Change your get recipes method to just return an observable and don't mark it as async.

getRecipes() {
  return this.http.get<any[]>('http://localhost:4200/recipes');
}

Then in your component assign the observable to a component property

recipeList$ = this.getRecipesService.getRecipes();

and in the template use the async pipe to subscribe to the observable

<ng-container *ngIf="recipeList$ | async as recipeList">
  {{ recipeList | json }}
</ng-container>

The async pipe immagically manages subscriptions for you.

Sign up to request clarification or add additional context in comments.

3 Comments

I followed your instructions and what gets displayed in the browser is: { "_isScalar": false, "source": { "_isScalar": false, "source": { "_isScalar": false, "source": { "_isScalar": false }, "operator": { "concurrent": 1 } }, "operator": {} }, "operator": {} }
whereas this is nice and direct, OP might want to note that there's no space for the this.recipes = response and so more resturcturing would be required...
@AdrianBrand Thanks again for your response. My solution was to return the observable as you noted and then subscribing to my getRecieps() method. Thanks a bunch
0

I'm not tooo certain w/ typescript, but I think the problem is that, in order of execution:

  1. getRecipesService.getRecipes() starts
  2. getRecipes puts a this.https.get onto the async stack then returns
  3. Since getRecipes has returned, getRecipes().then is called then returns
  4. The https request is finished
  5. this.https.get is pulled from the async stack and .subscribe() is called

whereas you want 1,2,4,5,3. What you need to do then is to get 3 to wait for 5. So you can try:

1 promises

(nb im a normal js coder not a typescript coder bc havent got my dev stack setup yet) An async function can return a promise, which will wait for a resolve function to be called before proceeding::

async getRecipes(){
   return new Promise ((resolve,reject)=>{
        this.http.get(url).subscribe(response=>{
            this.recipes = response;
            resolve(); // .then fires after this
        })
   })
}

At the cost of adding a few extra nested lines, this method doesn't change your code structure, but if you're willing to learn about await:

2 the await keyword

Inside async functions, you can await promises which will block the function from returning before the promise is fulfilled:

async getRecipes(){
   this.recipes = await this.http.get(url).toPromise();
}

further reading: https://medium.com/@balramchavan/using-async-await-feature-in-angular-587dd56fdc77

Comments

0

Thanks to everyone who commented, I really love this community!

For anyone who finds this post looking for a solution this is that I ended up with

    @Component({
    selector: 'recipes', //<recipes>
    template: `<h2>List of Recipes </h2>
                   <li *ngFor= "let recipe of recipeList">
                   {{recipe.recipeName}}
                   </li>
                `
})
export class RecipesComponent{
    constructor(getRecipesService: GetRecipesService){
        this.recipeList = getRecipesService.getRecipes().subscribe(promise=>{
            this.recipeList = promise;
        });
        
    }
    
    recipeList;

}

@NgModule()
export class GetRecipesService{
    recipes: any[];
    constructor(private http:HttpClient){
       this.http = http;
    }
    getRecipes(){
       return  this.http.get<any[]>('http://localhost:4200/recipes');
    }
}

Comments

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.