JavaScript中如何等待AJAX命令执行完成?
嘿,我太懂你现在的抓狂了!刚摸JavaScript的时候,异步AJAX这个坑真的特别容易踩——你调用那些year1fun、cc1fun这类AJAX函数时,JS根本不会乖乖停下来等服务器返回结果,直接就跳去执行后面的document.getElementById...代码了,完全打乱了你想要的执行顺序对吧?
为啥会这样?
这是因为AJAX默认是异步的,简单说就是JS把AJAX请求“甩”给浏览器去处理,自己接着往下跑代码,完全不等服务器响应回来。所以你的DOM赋值操作会在AJAX请求还没完成的时候就执行了,自然达不到预期效果。
怎么解决?
核心思路就是:让后续的DOM操作必须等AJAX请求完成后再执行,下面给你两种适合新手的方案,从基础到进阶都有:
方案1:用回调函数(基础款)
如果你的year1fun这类AJAX函数支持传入回调,就可以把要等AJAX完成后执行的代码塞进回调里。
首先修改你的AJAX函数,让它接受一个回调参数:
// 以year1fun为例,改造后支持回调 function year1fun(yearVal, a, callback) { const xhr = new XMLHttpRequest(); xhr.open('POST', '你的接口地址'); // 替换成你的实际接口 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onload = function() { if (xhr.status === 200) { // 服务器返回成功后,执行回调 callback(null, xhr.responseText); } else { callback(new Error('year请求失败')); } }; xhr.onerror = function() { callback(new Error('网络出错啦')); }; xhr.send(`year=${yearVal}&a=${a}`); } // cc1fun、tp1fun也按照同样的方式改造,加上callback参数
然后在putthedataback里,把DOM操作放到回调的嵌套里(确保顺序执行):
function putthedataback() { for (var i = 0; i < year.length; i++) { // 用立即执行函数解决var的作用域问题,避免循环变量混乱 (function(i) { var a = i + 1; // 先等year1fun完成,再执行cc1fun,接着tp1fun,最后更新DOM year1fun(year[i], a, function(err, res) { if (err) { console.error('year请求出错:', err); return; } cc1fun(cc[i], a, function(err, res) { if (err) { console.error('cc请求出错:', err); return; } tp1fun(tp[i], a, function(err, res) { if (err) { console.error('tp请求出错:', err); return; } // 所有AJAX都完成了!现在更新DOM document.getElementById("year" + a).value = "" + year[i]; document.getElementById("cc" + a).value = "" + cc[i]; // 这里补全你其他的DOM赋值代码 }); }); }); })(i); } }
方案2:用Promise + async/await(进阶款,更清晰)
回调嵌套多了容易变成“回调地狱”,现在更推荐用Promise配合async/await,代码可读性高很多。
首先把AJAX函数改成返回Promise:
// 改造year1fun为返回Promise function year1fun(yearVal, a) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('POST', '你的接口地址'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onload = function() { if (xhr.status === 200) { resolve(xhr.responseText); // 请求成功,把结果传出去 } else { reject(new Error('year请求失败')); // 请求失败,抛出错误 } }; xhr.onerror = function() { reject(new Error('网络出错啦')); }; xhr.send(`year=${yearVal}&a=${a}`); }); } // cc1fun、tp1fun也改成同样的返回Promise形式
然后把putthedataback改成async函数,用await等待每个AJAX完成:
async function putthedataback() { // 用let代替var,避免循环作用域问题 for (let i = 0; i < year.length; i++) { let a = i + 1; try { // 顺序等待三个AJAX请求完成 await year1fun(year[i], a); await cc1fun(cc[i], a); await tp1fun(tp[i], a); // 所有请求都完成了,再更新DOM document.getElementById("year" + a).value = "" + year[i]; document.getElementById("cc" + a).value = "" + cc[i]; // 补全其他DOM赋值代码 } catch (err) { // 捕获错误,避免一个请求失败导致整个循环中断 console.error(`第${a}组请求出错:`, err); } } }
如果这三个AJAX请求不需要顺序执行(可以同时发起),还能用Promise.all提高效率:
async function putthedataback() { for (let i = 0; i < year.length; i++) { let a = i + 1; try { // 同时发起三个请求,等所有请求都完成再继续 await Promise.all([ year1fun(year[i], a), cc1fun(cc[i], a), tp1fun(tp[i], a) ]); // 更新DOM document.getElementById("year" + a).value = "" + year[i]; document.getElementById("cc" + a).value = "" + cc[i]; // 补全其他DOM赋值代码 } catch (err) { console.error(`第${a}组请求出错:`, err); } } }
小提示
如果你用的是jQuery的$.ajax,它本身就支持Promise,直接用async/await就行,不用自己封装Promise哦~
内容的提问来源于stack exchange,提问作者Avon97

