引言:
在业务操作中,有一个数据是通过AJax请求回来的。而且这个数据在页面上会被操作。那么这个数据就需要存放到页面上,但是在通常的ajax方法是“异步”的,就会出现数据无法拿到。
那么我们来举个例子演示一下,页面上有两个按钮,一个查询上一个月的积分,一个查询这个月的积分。在加载的数据时候,将上个月和本月的数据都加载了。
<div id="btndiv" style="height:100px;background-color:blue;" align="center" >
<p id="jf" >积分:111</p>
<p><button id="btn">上个月</button> <button id="btn1">本月</button></p>
</div>
编写Ajax方法,请求后台查询积分。采用通常的ajax方法,也就是异步的ajax 方法 async:true ,默认为true,可以在请求的时候不写这个参数
$(function(){
alert("第一:"+$("#jf").text());
//定义一个全局变量
var responseData ;
//第一种方式: 将ajax 的异步请求设置为 false
var requestData = {"name":"admin","passwd":"admin1"};
$.ajax({
url: "./jfdata.json", // 模拟请求action后响应的json数据
data:requestData, // 请求的参数,可以是不同的形式
type: "GET", // 或者 POST
//dataType: "json", // 预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,这里设置为 json
async:true, // 特别注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
success: function(result){ //请求成功后的响应函数
//一般会强制将data转化为json对象
//var redata = eval("(" + result + ")");
responseData = result;
alert("第二"+result);
}
});
console.log("异步情况打印:"+responseData);
$("#jf").text(responseData == null?"值不存在":responseData.jf1);
alert("第三次:"+$("#jf").text());
//上一月的点击事件
$("#btn").click(function(){
$("#jf").text(responseData == null?"值不存在":responseData.jf2);
});
//上一月的点击事件
$("#btn1").click(function(){
$("#jf").text(responseData == null?"值不存在":responseData.jf1);
});
});
这个 url: "./jfdata.json" 走的是本地的json文件
{"jf1":888,"jf2":666}
jf1 是本月的积分,jf2 是上个月的
我创建了全局变量 var responseData ; 来存放ajax请求后的数据,然后在方法外使用
按照之前的ajax请求,看下出现的效果。 注意其中写的 alert 弹窗
第一个 alert 在ajax方法之前执行,读取初始化的设置的积分值 111
第二个 alert 跨过了 ajax 方法, 并没有执行ajax方法,就执行了 jf 的赋值
第三个 alert 执行了ajax方法,拿到了请求的数据
这说明,默认的ajax 方法是异步请求,在整个页面加载后,才执行了ajax方法。 这并不是我们需要的效果。
解决问题方法1:修改ajax方法为 同步请求,在加载页面时,就去执行ajax 方法
设置 ajax 请求的 async:false 即可。
特别注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
但是这样会导致浏览器出现假死的情况。
在jquery的中文api 上也说明了这一点。
强烈不建议把这个选项设置成false,这意味着所有的请求都不再是异步的了,这也会导致浏览器被锁死。
演示一下这样的情况:
假如我们需要在点击 “上个月” 的时候,请求后台,拿到一些其他数据,由于数据量比较大,请求耗时五秒。 并在请求时把 “上个月” 按钮改为 正在刷新,当数据成功返回改为“刷新成功”。 并且我们需要在 外部方法中使用这些数据,那么依然使用 同步的ajax 方法请求。
代码:
$(function(){
$("#btn").live('click', function() {
$("#btn").replaceWith('<button id="btn">正在刷新</button>');
$("#btndiv").css("background","red");
var responseData ;
var requestData = {"name":"admin","passwd":"admin1"};
$.ajax({
url: "http://localhost:8080/AJaxResult/AjaxServlet", // 模拟请求action后响应的json数据
data:requestData, // 请求的参数,可以是不同的形式
type: "GET", // 或者 POST
//dataType: "json", // 预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,这里设置为 json
async:false, // 特别注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
success: function(result){ //请求成功后的响应函数
responseData = result;
$("#jf").text("积分:"+responseData.totalRows);
$("#btn").replaceWith('<button id="btn">刷新成功</button>');
$("#btndiv").css("background","blue");
$("#jf").text("积分:"+responseData.totalRows);
}
});
//alert(""+responseData);
});
});
servlet:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String data ="{\"datas\":[{\"child\":[],\"content\":\"体育新闻体育新闻体育新闻\",\"id\":1,\"name\":\"体育新闻\",\"parent\":null},{\"child\":[],\"content\":\"娱乐新闻娱乐新闻娱乐新闻\",\"id\":2,\"name\":\"娱乐新闻\",\"parent\":null},{\"child\":[],\"content\":\"时政新闻时政新闻时政新闻\",\"id\":3,\"name\":\"时政新闻\",\"parent\":null}]," +
"\"pageNumber\":1,\"pageSize\":3,\"pageStartIndex\":0,\"totalPages\":8,\"totalRows\":22}";
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
System.out.println("执行线程");
}
}).start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
PrintWriter out = response.getWriter();
out.print(data);
}
先看下使用ajax异步的情况:(正确的显示,但是无法将数据拿到外部方法中使用) async:true
看下使用ajax同步的情况:(不正确的显示,但是可以将数据拿到外部方法中使用)async:false
我们发现,在点击上个月之后,没有动静,直到五秒之后,才将数据变化。
原因:浏览器的渲染(UI)线程和js线程是互斥的,在执行js耗时操作时,页面渲染会被阻塞掉。当我们执行异步ajax的时候没有问题,但当设置为同步请求时,其他的动作(ajax函数后面的代码,还有渲染线程)都会停止下来。即使我的DOM操作语句是在发起请求的前一句,这个同步请求也会“迅速”将UI线程阻塞,不给它执行的时间。
来源:https://www.cnblogs.com/zengguowang/p/6188059.html
所以,需要使用别的方法解决这个问题:
(1)setTimeout 来解决
详情:https://www.cnblogs.com/zengguowang/p/6188059.html
(2)将ajax同步的数据,赋值到页面的隐藏域中
这个就是在同步请求的方法中,将数据赋值到页面中的隐藏域中,如果再次使用该数据,从隐藏域中将数据再拿出来使用。
隐藏域:
<input type="hidden" id="data" value="" />
ajax请求得到的数据:
$("#data").val(data);
再次使用这个数据:
//1,从隐藏域中获取传回来的json数据
var data = $("#data").val();
但是这两种方法都不好。
最终解决办法:使用 jquery 提供的 Deferred
$(function(){
$("#btn").live('click', function() {
$("#btn").replaceWith('<button id="btn">正在刷新</button>');
$("#btndiv").css("background","red");
$.when(getData()).done(function(result){
//一般会强制将data转化为json对象
//var redata = eval("(" + result + ")");
var responseData = result;
$("#jf").text("积分:"+responseData.totalRows);
$("#btn").replaceWith('<button id="btn">刷新成功</button>');
$("#btndiv").css("background","blue");
$("#jf").text("积分:"+responseData.totalRows);
}).fail(function(){
alert("$.get 失败!");
});
});
function getData(){
var defer = $.Deferred(); //创建一个新的 Deferred(延迟)对象
//第一种方式: 将ajax 的异步请求设置为 false
var requestData = {
"name": "admin",
"passwd": "admin1"
};
$.ajax({
url: "http://localhost:8080/AJaxResult/AjaxServlet", // 模拟请求action后响应的json数据
data: requestData, // 请求的参数,可以是不同的形式
type: "GET", // 或者 POST
//dataType: "json", // 预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,这里设置为 json
async: true, // 特别注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
success: function(result){
defer.resolve(result); //解决Deferred(延迟)对象,并根据给定的参数调用任何 doneCallbacks 回调函数
//这个函数会在 延迟对象即ajax 执行完毕后,在调用回调函数的时候 将参数值 result 传递给回调方法
}
});
return defer.promise(); // 当ajax执行完毕,返回 Deferred 对象
//它创建一个promise对象,其目的是在未来某个时间点返回一个响应。
}
});
主要的步骤:
var defer = $.Deferred(); //创建一个新的 Deferred(延迟)对象
defer.resolve(data); // data 异步的ajax响应的数据
return defer.promise(); // 当ajax执行完毕,返回 Deferred 对象
$.when(getData()).done(function(result){ // 在ajax请求完后调用
responseData = result; // 将拿到的数据赋值到全局变量
}).fail(function(){ // 失败时调用
});
});
什么是deferred对象?
开发网站的过程中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。
通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。
但是,在回调函数方面,jQuery的功能非常弱。为了改变这一点,jQuery开发团队就设计了deferred对象。
简单说,deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意思是"延迟",所以deferred对象的含义就是"延迟"到未来某个点再执行。
来源:http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object
具体的细节,参考这几个网址:
https://www.cnblogs.com/zengguowang/p/6188059.html
https://segmentfault.com/a/1190000007216755
http://www.cnblogs.com/panmy/p/5651732.html
http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object
https://www.cnblogs.com/panmy/p/5651732.html
看下效果: