I require assistance in comprehending async/await in order to ensure that the code will pause and wait for my http.get function to

I've been reading various articles and forums, both here and on Google, about using awaits and asyncs in my code. However, no matter where I place them, the code after my http.get call always executes first. The alert messages from the calling program also don't show up after I call my service. Can someone please explain to me in simple terms what I need to do? I'm new to await/async and would appreciate any help you can provide.

The order of alerts appearing on the page is as follows:

  1. single-game.component.ts: "first time in the single game component.....")

  2. games.service.ts: "Do we get past the call/subscribe?"

  3. games.service.ts: "Service --> current game is..." The alert shows the returned JSON.

Message 1 should appear first, followed by message 3, and then message 2. The alerts in single-game.component.getCurrentGame that come after calling the service should appear next.

single-game.component.ts code:

  getCurrentGame() : void {
    const id = Number(this.route.snapshot.paramMap.get('id'));
    alert (`first time in the single game component and the value returned is ${this.currentGame?.Name}`)
    this.gamesService.getCurrentGame(id).subscribe(value => this.currentGame = value);

    // Neither of these two alerts ever show.
    alert (`back in the single game component and the value returned is ${this.currentGame?.Name}`)
    alert(`we're back but the above message didn't show`);    
  }

games-service.ts code

  getCurrentGame(gameId: number): Observable<Game> {
    game: GamesService;
    var url:string = this.gamesURLBase + `/GetSingleGameInfo/${gameId}`;
    var currentGame: Game |undefined;
    var strCurrentGame: string;

    this.http.get<Game[]>(url)
        .subscribe((value:Game[]) => {
          if (value.length > 0) {
            currentGame = value.find(element => element.GameId == gameId)
            alert(`Service --> currentGame is ${JSON.stringify(currentGame)}`)
          }; 
       })
    alert("Do we get past the call/subscribe?")
    return currentGame as unknown as Observable<Game>; 
  }

Thank you for taking the time to read this. I appreciate any insights or guidance you can offer.

Answer №1

After some troubleshooting, I finally discovered my mistake. It turns out that I was not misinterpreting async operations as I had initially thought. The issue actually stemmed from the service returning a one element array with a game inside, rather than just a single game. This highlighted the disparity between working with a real service versus a simplified tutorial example. For those interested, here is the tutorial link I referenced: https://angular.io/tutorial/tour-of-heroes/toh-pt4

With that realization, I made necessary adjustments to the files and removed all unnecessary alerts. I specifically modified the code to extract the 0th element of the array returned by the service and assign it to 'currentGame'. Now, I am able to display the name and other details on my HTML page successfully. Hooray for progress!

A special thanks goes out to @JoosepParts for generously offering assistance to someone new in this field.

single-game.component.ts

import { Component, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

import { ActivatedRoute, ResolveEnd } from '@angular/router';
import { Game } from '../../interfaces/games';
import { Question } from 'src/interfaces/questions';
import { Answer } from 'src/interfaces/answers';
import { GamesService } from 'src/services/games.service';
import { JsonPipe } from '@angular/common';


@Component({
  selector: 'app-single-game',
  templateUrl: './single-game.component.html',
  styleUrls: ['./single-game.component.css']
})
export class SingleGameComponent {

@Input() game?: Game;
questions: Question[] = [];
answers: Answer[] = [];
currentGame!: Game;

constructor(
  private gamesService: GamesService,
  private route: ActivatedRoute,
) {}

   ngOnInit(): void {
    this.getCurrentGame();
  }

  getCurrentGame(): void {
    const id = Number(this.route.snapshot.paramMap.get('id'));
    this.gamesService.getCurrentGame(id)
      .subscribe(currentGame => {
        this.currentGame = currentGame[0] // <-- this is what made the difference
    }); 
  }
}

games.service.ts

import { Injectable, OnInit, Pipe } from '@angular/core';
import { Observable, of, throwError } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import {catchError, map, tap} from 'rxjs/operators'


import { Game } from '../interfaces/games';
import { Question } from 'src/interfaces/questions';
import { Answer } from 'src/interfaces/answers';
import { ErrorHandlingService } from './error-handling.service';
import { JsonPipe } from '@angular/common';

@Injectable({
  providedIn: 'root'
})
export class GamesService {

  errHandler: ErrorHandlingService;

  constructor(private http: HttpClient) {
    this.errHandler = new ErrorHandlingService();
}

  private gamesURLBase = 'http://localhost:5000/api/GamePlay';

  /* All Games */
  getGames(): Observable<Game[]> {
    var url:string = this.gamesURLBase + '/GetAllGames';

    return this.http.get<Game[]>(url)
      .pipe(
        catchError(this.errHandler.handleError<Game[]>('getGames', []))
      );
  }

  getCurrentGame(gameId: number): Observable<Game[]> {
    var url:string = this.gamesURLBase + `/GetSingleGameInfo/${gameId}`;
    
    return this.http.get<Game[]>(url)
      .pipe(
//        tap (data => alert(`in tap of http call and I can see ${JSON.stringify(data)} `)),
        catchError(this.errHandler.handleError<Game[]>('getCurrentGame'))
    );
  }
}

Answer №2

Typically, the goal appears to be transforming asynchronous code to mimic synchronous functionality, resulting in peculiar workarounds as outlined in your description. Thankfully, Promises are non-blocking, ensuring that the entirety of your JavaScript code stack remains unaffected by the promise's operation, while still executing like a synchronous code block.

async getCurrentGame(): Promise<void> {
    const id = Number(this.route.snapshot.paramMap.get('id'));
    alert(`first time in the single game component and the value returned is ${this.currentGame?.Name}`);

    this.currentGame = await this.getGame(id);

    alert(`back in the single game component and the value returned is ${this.currentGame?.Name}`);
    alert(`we're back but the above message didn't show`);    
}

getGame(id: number): Promise<Game> {
    return new Promise((resolve, reject) => {
        this.gamesService.getCurrentGame(id).subscribe(value => resolve(value), error => reject(error));
    });
}

Alternatively, consider exploring the reactive approach.

currentGame$: Observable<Game>;

getCurrentGame(): void {
    const id = Number(this.route.snapshot.paramMap.get('id'));
    this.currentGame$ = this.gamesService.getCurrentGame(id);
}
<div *ngIf="currentGame$ | async as currentGame">
    <p>{{ currentGame.gameProperty }}</p>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The 'Set-Cookie' response header failed to be recognized for a subsequent HTTP request

When a user successfully logs in, their information is stored in a cookie using the "Set-Cookie" response header. However, I am facing an issue where the cookie seems to get lost when making subsequent requests from the client. As a result, the server trea ...

Angular2: Number types being converted to strings by the + operator

Why is the '+' operator in my binding treating variables as strings instead of numbers, unlike other arithmetic operators like -, * and / which correctly perform arithmetic operations on them as numbers, as expected based on their types in the as ...

Exploring Page Navigation Techniques in Angular 2

Exploring the world of Angular 2, I've come across a task to implement pagination. In my research, it led me to realize that the pagination logic must be coded in systems.config.js. My query now is locating the elusive file systems.config.js. What pur ...

Guide to executing Jest tests with code coverage for a single file

Instead of seeing coverage reports for all files in the application, I only want to focus on one file that I am currently working on. The overwhelming nature of sifting through a full table of coverage for every file makes it difficult to pinpoint the spe ...

Observe how Observable breaks down strings into individual letters (like in Karma and Angular)

Attempting to simulate an HTTP service in Angular tests (Karma), I included the following in the providers array: { provide: service, useValue: { getData: () => new Observable((subscriber) => { subscriber.next('i ...

Should the RxJS interval() function be employed to receive live server updates?

In my Angular application, I implemented this code to continuously check for updates. export class RealTimeComponent { products$:any; constructor(private service: ProductService){} ngOnInit(){ this.products$ = interval(5000) .pipe( ...

I am interested in creating a get and set method using the arrow function

Currently utilizing typescript 3.1.6. Is it possible to define get and set methods using arrow functions like this: export class foo { /* attributures ... */ /* constructor ... */ /* ---- Example ---- */ get bar = ():string => this.anAttributeToGe ...

Struggle between Angular and fundamental CSS principles

Upon following the steps below, I aim to achieve my desired grids: How to set auto-margin boxes in flexible-width design using CSS? The solution provided is effective when implemented outside of Angular. However, when inserted inside an Angular component ...

Issue encountered: Cannot locate module: Error message - Unable to find 'stream' in 'C:devjszip-test ode_modulesjsziplib' folder

I am encountering an issue in my angular 7 application while using jszip v3.2.1. During the project build process (e.g., running npm start), I receive the following error message: ERROR in ./node_modules/jszip/lib/readable-stream-browser.js Module not fo ...

Issue encountered with executing `ng build` in Angular Bitbucket Pipeline

I have set up a pipeline in Bitbucket to deploy my Angular app to an FTP server. The pipeline.yml file for this setup looks like this: image: node:6.9.4 # we need node image to run our angular application in clone: # help to clone our source here de ...

How to empty an array once all its elements have been displayed

My query pertains specifically to Angular/Typescript. I have an array containing elements that I am displaying on an HTML page, but the code is not finalized yet. Here is an excerpt: Typescript import { Component, Input, NgZone, OnInit } from '@angul ...

The data type 'string' cannot be assigned to the data type 'Position'

Currently, I am in the process of converting React js to typescript. The component being used is a Class Component. I would like to obtain CSS settings through props and apply them to an element. How can I resolve this issue? render(){return( <span st ...

Switching Facebook accounts on Firebase

I'm currently working on an Angular2 App that utilizes Firebase as its User system, with authentication providers including Email + Password, Facebook, and Google. One issue I have encountered is that when logging in with Facebook, I am unable to swi ...

Obtaining data from a nested JSON using Angular4 and AngularFire2's db.list

I have a restaurant and I wanted to share a tip: Here is the JSON Structure: http://prntscr.com/gn5de8 Initially, I attempted to retrieve data from my restaurant as follows: <p *ngFor="let tip of restaurants[item.$key].tips" [innerHTML]=&qu ...

Unresolved conflict stemming from an HTML closing tag in the index.html file, despite both source and destination files being identical

I am currently facing a challenge while trying to merge my code with the master branch through a pull request. The conflict arises in the src/index.html file, specifically on line 17 which states </html> needs to be corrected to </html>. It&apo ...

Exploring the potential of Vue with Router to create a dynamic multi-page

Struggling to come up with a suitable title for this Vue project dilemma. Here's what I'm facing: Recently started using Router in my Vue project and feeling quite lost. The setup in App.vue simply includes <RouterView>, which seems stra ...

Tips for properly executing directives in sequential order while using async in Angular 13

I created a standard registration form using Angular and implemented an async directive (UserExistsDirective) to validate if the email is already taken. To manage error messages, I also utilized a second directive (ValidityStyleDirective) which is also use ...

Discover the secrets to manipulating form controls within an ngFor loop in Angular

In my reactive form, I have a list of mat-slide-toggle that I want to use. Here is what I tried: HTML>>>>>>> <form [formGroup]="notificationForm"> <div class="row"> <div class=&quo ...

Ways to populate missing cells with a default hyphen symbol

Looking for a way to default empty cells in my primeng datatable to '-'. Consider the following data: [ { 'column': null }, { 'column': { 'name': 'A' } }, { 'column': { 'name': ...

How about utilizing React's conditional rendering feature?

I'm currently working on a component that displays tournaments and matches, and I'm facing a challenge in implementing a filter option for users to select tournaments by 'league', while still displaying all tournaments if no 'leagu ...