如何从异步ajax方法中正确获取返回值
问题根源
你始终拿到undefined的核心原因有两点:
- 回调函数内部的
return只会终止回调函数自身的执行、返回值到回调的调用位置,不会向外层的setAvailableQuestions函数返回值,所以外层函数拿不到结果 - 你写的同步请求版本逻辑顺序错误:同步请求不需要绑定
onreadystatechange事件,事件回调里的返回值同样不会被外层函数接收,自然拿到undefined
可行解决方案
方案1:沿用原有回调封装,直接在回调内写业务逻辑
这是改动最小的方案,不需要修改你已经写好的ajaxRequest封装,只需要把依赖请求结果的逻辑全部放到回调函数内部执行,不要尝试在外层接返回值:
// 原有ajaxRequest封装无需改动 function ajaxRequest(type,url,callback,data=""){ let request = new XMLHttpRequest(); request.open(type, url); request.onload = function () { switch (request.status){ case 200: case 201: callback(request.responseText); break; default: console.log(request.status); } }; request.send(data); } function getNewQuestion(){ // 直接把数据处理逻辑作为回调传入 ajaxRequest("GET","../controller.php?func=get_availableQuestion", (questions) => { const availableQuestions = JSON.parse(questions); console.log(availableQuestions); // 此处可以正常拿到解析后的数据 // 所有需要用到availableQuestions的业务逻辑,全部写在这个回调内部 }); }
注意:不要在回调函数外部写依赖请求结果的代码,ajax默认是异步执行,回调外的代码会在请求还没返回时就提前运行,必然拿不到值。
方案2:封装Promise,配合async/await写法
这就是你提到的.then/await实现方案,把ajax请求封装成Promise实例后,就可以用同步写法写异步逻辑,避免回调嵌套:
// 改造ajaxRequest为Promise版本 function ajaxRequest(type,url,data=""){ return new Promise((resolve, reject) => { let request = new XMLHttpRequest(); request.open(type, url); request.onload = function () { switch (request.status){ case 200: case 201: resolve(request.responseText); // 请求成功把结果传给resolve break; default: reject(new Error(`请求异常,状态码:${request.status}`)); } }; request.onerror = () => reject(new Error("网络连接失败")); request.send(data); }) } // 用async标记函数,内部用await等待请求结果 async function getNewQuestion(){ try { const questionStr = await ajaxRequest("GET","../controller.php?func=get_availableQuestion"); const availableQuestions = JSON.parse(questionStr); console.log(availableQuestions); // 正常拿到数据 // 后续业务逻辑直接往下写即可,不需要嵌套回调 } catch (err) { console.error("获取题目失败:", err); } } // 调用函数即可执行 getNewQuestion();
你之前写的同步请求为什么不生效?
同步请求的执行逻辑是request.send()调用后会阻塞后续代码执行,直到请求完成,所以不需要绑定状态监听事件,直接在send之后读取响应即可。你之前把解析逻辑写在onreadystatechange回调里,return的值只会留在回调作用域,外层函数拿不到。
正确的同步写法参考(不推荐实际使用):
// 同步请求会在请求过程中锁死整个页面,用户无法做任何交互,体验极差,仅做原理演示 function setAvailableQuestions(){ const ajax = new XMLHttpRequest(); // 第三个参数传false代表同步请求 ajax.open("GET","../controller.php?func=get_availableQuestion", false); ajax.send(); if(ajax.status === 200 || ajax.status === 201) { return JSON.parse(ajax.response); } console.log("请求失败,状态码:", ajax.status); return null; } function getNewQuestion(){ const availableQuestions = setAvailableQuestions(); console.log(availableQuestions); }
日常开发优先用前两种异步方案,不要用同步请求。
内容的提问来源于stack exchange,提问作者chubby marschmallow
相关产品推荐
相关产品推荐

