-1

TS Code:

var APIres: any;

export class Component implements OnInit {

 async getInfo(){  
  await this.apicall();
  console.log('API response', APIres)

 }
 
 async apicall(){
   var request = new XMLHttpRequest(); 
   request.open('POST', 'URL', true);
   request.setRequestHeader('Content-Type', 'application/json');
   request.setRequestHeader('Authorization', 'Bearer ' + accessToken);
   request.onreadystatechange = async function () {
      console.log('Got Values')
      await res(JSON.parse(this.responseText));
   }
   request.send(content);
 }
}

async function res(x){console.log('out');APIres = x} //Outside of export

Output:

API response undefined
Got Values
out

Desire Output:

Got Values
out
API response res
R. Baraiya
  • 1,490
  • 1
  • 4
  • 17
  • 3
    `apicall` does not return a promise. You *could* promisify XMLHttpRequest but there is probably no point, instead you should use `fetch()`. Raw XHR has been essentially obsolete for over a decade now. – VLAZ Jan 27 '23 at 12:42
  • 3
    @VLAZ - It does return a promise (it's an `async` function), but that promise isn't in any way connected to the XHR... :-) – T.J. Crowder Jan 27 '23 at 12:43
  • 3
    Also - it's *very bad practice* to ferry async data through a global variable. It should be returned as the end result of the promise. Or the older way was to use the callback. – VLAZ Jan 27 '23 at 12:44

2 Answers2

3

Adding async doesn't magically cause the function to wait for an event

Wrap your function with a promise

var APIres: any;

export class Component implements OnInit {
  async getInfo() {
    await this.apicall();
    console.log("API response", APIres);
  }

  apicall() {
    return new Promise((resolve) => {
      var request = new XMLHttpRequest();
      request.open("POST", "URL", true);
      request.setRequestHeader("Content-Type", "application/json");
      request.setRequestHeader("Authorization", "Bearer " + accessToken);
      request.onreadystatechange = function () {
        console.log("Got Values");
        resolve(res(JSON.parse(this.responseText)));
      };
      request.send(content);
    });
  }
}

async function res(x) {
  console.log("out");
  APIres = x;
} //Outside of export
Konrad
  • 21,590
  • 4
  • 28
  • 64
3

Nothing in the code connects the completion of the XHR call with the promise that apicall returns. Although you could wrap XHR in a promise, it makes more sense to use the modern replacement, fetch.

Separately, as VLAZ pointed out in a comment, it's poor practice to populate global data as a side-effect of a method call. Instead, have the methods return the necessary data.

Here's an example using fetch:

export class Component implements OnInit {
    async getInfo() {
        // Note receiving the data as the return value of the method
        const apiData = await this.apicall();
        console.log("API response", apiData);
        return apiData; // Provide the data to the caller
    }

    async apicall() {
        // `fetch` returns a promise of a Response object
        const response = await fetch("/url/here", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                Authorization: "Bearer " + accessToken,
            },
            body: content,
        });
        // If the response wasn't okay...
        if (!response.ok) {
            // ...fail with an error
            throw new Error(`HTTP error ${response.status}`);
        }
        // The response was okay, read and parse the JSON
        return await response.json();
    }
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Hi TJ, the code working good, is it possible to receive response text with it ? like this response https://learn.microsoft.com/en-us/graph/api/user-post-events?view=graph-rest-1.0&tabs=javascript. Many Thanks – R. Baraiya Jan 27 '23 at 13:08
  • 2
    You can get the text with `.text()` instead of `.json()`. However, from what I've seen, the API you use uses JSON as a response schema, so I think `.json()` fits well – A_A Jan 27 '23 at 13:23