Woodsman
Woodsman

Reputation: 1179

Force Javascript/Node to wait for service completion

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.

Upvotes: 1

Views: 58

Answers (1)

programoholic
programoholic

Reputation: 5194

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

Upvotes: 2

Related Questions