【问题标题】:Async/Await, returned variable is undefined after XHR request异步/等待,XHR 请求后返回的变量未定义
【发布时间】:2026-01-17 22:00:02
【问题描述】:

我正在尝试从 API 获取公共 IP 地址,然后将该 IP 用于另一个函数 (ninjaUniqueVisitorRequest())。

我有以下代码:

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);
}

目前,当我运行 ninjaUniqueVisitorRequest(); 时,console.log(ninjaPublicIp); 返回 undefined

我有点理解它会在发出请求之前立即返回,但这就是我认为我使用async/await 修复的问题。

感谢任何想法! PHP的家伙在这里,放轻松。

【问题讨论】:

  • 你的 ninjaGetIp() 函数不是 async 并且不返回 Promise。
  • 我以为你只是把async 放在你调用的函数上。同样据我了解return xyz 无论如何都会转换为承诺?我正在用 Babel 编译。
  • 如果你想要基于承诺的 AJAX 使用 fetch() API。
  • @KriiV 如果你有 any return 并且你用async 标记它,它将被转换为一个承诺。 ninjaGetIp 不返回任何内容。

标签: javascript async-await


【解决方案1】:

目前您的ninjaGetIp 不是可等待的Promise

您可以尝试只返回一个新的 Promise 来包装实现,或者只是在函数前添加 async 关键字。

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 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'; 
                }
            }
        
    }

【讨论】:

    【解决方案2】:

    那是因为ninjaGetIp 是不可等待的。您必须返回 Promise 才能使用 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');
                    }
                }
            });
    
        }
    

    此外,您可以简化它并使用返回承诺的fetch,而不是使用构建XMLHttpRequest 所需的所有代码:

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

    TL;DR;

    如果你想坚持使用XMLHttpRequest,我会为它创建一个包装器,因为它有很多冗余代码:这是一个示例:

    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);
            });
        }
    }
    

    用法

    const httpClient = new HttpClient();
    const data = await httpClient.get('some url');
    
    1. Fetch API
    2. Async
    3. Await

    【讨论】:

    • 我将 XHR 用于兼容性目的...但不确定这是否正确。
    • XHR 仍然具有fetch 中不存在的功能 - 所以,使用 XHR 是完美的 - 但是,问题中的代码不使用任何这些功能
    • 如果响应中有错误代码,您的 HttpClient 会留下未解决的承诺。您还重复了大量可以在 get()post() 方法之间轻松共享的代码。
    • 同意。在这方面有很多工作要做,以使其为生产做好准备。这是一个快速示例,说明如何抛出他现有的代码并将其包装在一个类中。更不用说它缺少其余的 HTTP 动词(PATCH、DELETE、PUT 等......)
    最近更新 更多