Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async/Await, returned variable is undefined after XHR request

I am attempting to get a public IP address from an API and then having that IP to be used inside another function (ninjaUniqueVisitorRequest()).

I have the following code:

function ninjaGetIp() {
    var ipRequest = new XMLHttpRequest();
    ipRequest.open('GET', "https://api.ipify.org?format=jsonp=", true);
    ipRequest.send();
    ipRequest.onload = function () {
        if (ipRequest.status >= 200 && ipRequest.status < 400) { // If response is all good...
            return ipRequest.responseText;
        } else {
            console.log('There was an error retrieving the public IP.');
            return '127.0.0.1'; 
        }
    }
}

async function ninjaUniqueVisitorRequest() {
    // var ninjaSiteUuid = ninjaGetSiteUuid();
    // var ninjaFingerprint = await ninjaGetFingerprint();
    var ninjaPublicIp = await ninjaGetIp();
    console.log(ninjaPublicIp);
}

Currently, when I run ninjaUniqueVisitorRequest(); the console.log(ninjaPublicIp); returns undefined.

I kind of understand that it returns straight away before the request has been made however thats what I thought I was fixing by using async/await.

Any ideas are appreciated! PHP guy over here, go easy on me.

like image 257
KriiV Avatar asked Jan 25 '19 00:01

KriiV


Video Answer


2 Answers

Currently your ninjaGetIp is not an awaitable Promise.

You can try just returning a new Promise that wraps the implementation or just add async keyword before the function.

function ninjaGetIp() {
    return new Promise(function (resolve, reject) {
        var ipRequest = new XMLHttpRequest();
        ipRequest.open('GET', "https://api.ipify.org?format=jsonp=", true);
        ipRequest.send();
        ipRequest.onload = function () {
            if (ipRequest.status >= 200 && ipRequest.status < 400) { // If response is all good...
                return resolve(ipRequest.responseText);
            } else {
                console.log('There was an error retrieving the public IP.');
                return resolve('127.0.0.1'); 
            }
        }
    });
    
}

Async sample

async function ninjaGetIp() {
            var ipRequest = new XMLHttpRequest();
            ipRequest.open('GET', "https://api.ipify.org?format=jsonp=", true);
            ipRequest.send();
            ipRequest.onload = function () {
                if (ipRequest.status >= 200 && ipRequest.status < 400) { // If response is all good...
                    return ipRequest.responseText;
                } else {
                    console.log('There was an error retrieving the public IP.');
                    return '127.0.0.1'; 
                }
            }
        
    }
like image 185
Jojo Narte Avatar answered Oct 16 '22 20:10

Jojo Narte


That's because ninjaGetIp is not awaitable. You have to return a Promise in order to be able to await.

   async function ninjaGetIp() {
        return new Promise( (resolve, reject) => {
            var ipRequest = new XMLHttpRequest();
            ipRequest.open('GET', "https://api.ipify.org?format=jsonp=", true);
            ipRequest.send();
            ipRequest.onload = () => {
                if (ipRequest.status >= 200 && ipRequest.status < 400) { // If response is all good...
                    resolve(ipRequest.responseText);
                } else {
                    console.log('There was an error retrieving the public IP.');
                    reject('127.0.0.1');
                }
            }
        });

    }

Also, you can simplify it and use fetch which returns a promise instead of using all the code required to build up an XMLHttpRequest:

async function ninjaGetIp() {
    return fetch('https://api.ipify.org?format=jsonp=');
}

TL;DR;

If you want to stick with XMLHttpRequest, I would create a wrapper for it because it's a lot of redundant code: Here's an example:

class HttpClient {
    constructor(){}
    async get(url) {
        return new Promise( (resolve, reject) => {
            const xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = (evt) => {
                if (evt.currentTarget.readyState === 4 && evt.currentTarget.status === 200) {
                    try {
                        const response = JSON.parse(evt.currentTarget.response);
                        resolve(response);
                    } catch (exception) {
                        reject(exception);
                    }
                }
            };
            xhttp.open('GET', url, true);
            xhttp.send();
        });
    }
    async post(url, data) {
        return new Promise( (resolve, reject) => {
            const xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = (evt) => {
                if (evt.currentTarget.readyState === 4 && evt.currentTarget.status === 200) {
                    try {
                        const response = JSON.parse(evt.currentTarget.response);
                        resolve(response);
                    } catch (exception) {
                        reject(exception);
                    }
                }
            };
            xhttp.open('POST', url, true);
            xhttp.send(data);
        });
    }
}

Usage

const httpClient = new HttpClient();
const data = await httpClient.get('some url');
  1. Fetch API
  2. Async
  3. Await
like image 1
mwilson Avatar answered Oct 16 '22 19:10

mwilson