异步/等待,XHR 请求后返回的变量未定义

Posted

技术标签:

【中文标题】异步/等待,XHR 请求后返回的变量未定义【英文标题】:Async/Await, returned variable is undefined after XHR request 【发布时间】:2019-06-18 19:43:20 【问题描述】:

我正在尝试从 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 不返回任何内容。 How do I return the response from an asynchronous call?的可能重复 【参考方案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');
    Fetch API Async Await

【讨论】:

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

以上是关于异步/等待,XHR 请求后返回的变量未定义的主要内容,如果未能解决你的问题,请参考以下文章

使用 Chrome + XHR 时未定义 PHP POST 变量

执行异步forEach循环后重新定义全局变量[重复]

等待将undefined返回到异步函数(var all_courses未定义)

异步函数总是返回未定义

VueJS:仅在计算内部未定义变量

为啥这个异步函数返回未定义? [复制]