1

I'm trying to make a call to a service and build an object with the resulting value. If I knew what I was doing, I'd also handle errors, but I don't know how. Unfortunately, the code begins the service call, then returns to the statement following the service before the service call finishes.

I don't care how un-modern, un-cool or whatever it is, I want this code to wait for the service to succeed or fail. Everything I do in the app depends on this service succeeding anyway.

I both tried setting breakpoints and as adding logging statements.

import { Injectable, Injector } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { UUID } from 'angular2-uuid';
import { Constants } from './constants';

@Injectable()
export class AppConfigService {

    private constants = null;

    constructor (private injector: Injector) { }

    loadAppConfig() {
        let http = this.injector.get(HttpClient);
        /*
        return http.get('/assets/app-config.json')
        .toPromise()
        .then(data => {
            this.appConfig = data;
        })
        */
        if(this.constants != null) {
            console.log('@@WOODSMAN environmentData is not null');
            console.log('@@WOODSMAN lazy loaded environmentData already is '+JSON.stringify(this.constants));    
        } else {
            console.log('@@WOODSMAN environmentData is null');   

            let headerValues = new HttpHeaders()
                .set('Accept-Encoding', 'application/json; charset=UTF-8')
                .set('Content-Type', 'application/json; charset=UTF-8')
                .set('SC_TRACE_ID', UUID.UUID());
            let httpOptions = {
                headers: headerValues
            };
            console.log('@@WOODSMAN: Waiting for environment info from service call');

            let appConfig = http.post("/proxy/report/getEnvironment", "",httpOptions)
                .toPromise().then(data => { 
                this.setConstants(data);
            });


/*
            while(this.environmentData == null) {
                console.log('@@@WOODSMAN appConfig is null even after service call');
            }
*/
            if(this.constants == null) {
                console.log('@@@WOODSMAN appConfig is null even after service call');
            }
        };
        console.log('@@WOODSMAN: environmentData result = '+JSON.stringify(this.constants));
    }

    private setConstants(environmentData) {
        console.log('@@WOODSMAN calling environmentData='+JSON.stringify(environmentData));
        this.constants = new (environmentData);
    }

    get config() {
        //return this.appConfig;
        if(this.constants==null) {
            console.log('@@WOODSMAN Call to config found null appConfig.  Calling the load');
            this.loadAppConfig();
            if(this.constants==null) {
                console.log('@@WOODSMAN Second call to config still found null appConfig.');
            }
        }
        console.log('@@WOODSMAN environment.salesconnectURL' + this.constants["baseUrl"]);
        console.log('@@WOODSMAN environment.salesconnectURL' + this.constants["homepageURL"]);
        console.log('@@@WOODSMAN end getConstants ');
        return this.constants;
    }

}

I see in the browser log these two lines in this order (with no log statements between them). '@@WOODSMAN: Waiting for environment info from service call' '@@@WOODSMAN appConfig is null even after service call'

I tried wrapping this in an async function and adding an await statement, but it still ignored that and did not wait for completion.

Since the service takes milliseconds to complete, I tried adding a while loop above to wait until the value got set. However, it became a forever loop as the service handler was not allowed to satisfy the loop condition.

*** Update

I tried adding a query call which offers a synchronous capability. So I coded the following:

            let ajaxObject = {
                type: "POST"
                ,headers: {
                     'Accept-Encoding' : 'application/json; charset=UTF-8'
                    ,'Content-Type' : 'application/json; charset=UTF-8'
                    ,'SC_TRACE_ID' : UUID.UUID()
                }
                ,url: "/proxy/report/getEnvironment"
                ,async : false
                ,data: "" 
                ,success: function(data) {
                    this.status="SUCCESS";
                    this.dataReturned = data;
                    console.log('@@WOODSMAN success reached');
                }
                ,error: function(jqXHR, textStatus, errorThrown) {
                    this.status="FAILURE";
                    this.jqXHR = jqXHR;
                    this.textStatus = textStatus;
                    this.errorThrown = errorThrown;
                    console.log('@@WOODSMAN failure reached');
                    console.log('Error on call to getEnvironment.  '+textStatus+' '+errorThrown);
                }
                ,status: "NOT_RUN"
                ,dataReturned : null
                ,jqXHR: {}
                ,textStatus: {}
                ,errorThrown: {} 
            };
            $.ajax(ajaxObject);
            console.log('@@@WOODSMAN after ajax service call' + JSON.stringify(ajaxObject));

When I run this, I get '@@@WOODSMAN after ajax service call' with the status being 'NOT_RUN'; This means that at the point the call is made, it did not execute either the success or the failure functions. Note that the request had async: false, set, so it has no excuse for doing an asynchronous call.

Is it possible to do socket programming in Javascript? It's becoming apparent that making a synchronous call is incredibly difficult anymore.

Woodsman
  • 901
  • 21
  • 61

1 Answers1

2

You should know that JS is Asynchronous , Means any code that deals with WEB API , It wont't wait for it to get complete , instead it will keep executing the next line of codes.

In the snippet shared by You , the lines

let appConfig = http.post("/proxy/report/getEnvironment", "",httpOptions)
                .toPromise().then(data => { 
                this.setConstants(data);
            });

is asynchronous. Hence JS engine won't wait for it to get complete and it will execute the next line of code . that is :

if(this.constants == null) {
                console.log('@@@WOODSMAN appConfig is null even after service call');
            }

Since above lines gets executed before the service call is finished, the value of this.constants is null that time . This is the reason you are able to see the last statement in the console.

To check Whether this.constants is null even after the service call , you can modify the code as below :

let appConfig = http.post("/proxy/report/getEnvironment", "",httpOptions)
                .toPromise().then(data => { 
                this.setConstants(data);
                if(this.constants == null) {
                console.log('@@@WOODSMAN appConfig is null even after service call');
            }
        });

** update. :

this is the way you should implement async-await. :

public  async loadAppConfig() {
        let http = this.injector.get(HttpClient);

            let appConfig = await http.post("url",{options})
                .toPromise().then(data => { 
                  .... next lines
            });

           console.log('this line will not execute until await is finished');

    } 

Here is the stackblitz demo. : demo

programoholic
  • 4,830
  • 5
  • 20
  • 59
  • I've seen different takes on the synchronous/async stuff for Javascript. On stack overflow, here's one such link I saw earlier: https://stackoverflow.com/questions/2035645/when-is-javascript-synchronous. Another person on Medium claimed that using the toPromise() converts the Observable into a Promise, which then forces it to wait for completion. Only half of that appears to be true. The "if" test above is just to prove a point. I need something, anything, that allows me to base what I do on the value coming back from a service. – Woodsman Mar 26 '20 at 19:23
  • So what is your doubt then ? Don't unnecessarily complicate things. Whether it is observable or Promises, they are used to handle async calls. None of them changes the way JS behaves. If you want to check the result once the async call gets finished , you will have to check inside there callbacks. Every method has their own callback handling machenism. What I understood is , you want to execute your code serially line by line . You can easily do that by implementing async-await. – programoholic Mar 26 '20 at 20:48
  • Maybe you didn't see it, but I wrapped that into a async function, but it still didn't listen and made it asynchronous. – Woodsman Mar 27 '20 at 00:35
  • I didn't see any where you used `aync-await` . Updated my answer – programoholic Mar 27 '20 at 06:08
  • 1
    Thank you @programoholic. I'm not sure what I did differently than you, but I'm now getting it to wait. I could fill this box with thank yous. – Woodsman Mar 27 '20 at 22:04