我在之前讲过Ajax的问题,但是并没有特别突出ajax的异步性.这里用阮一峰老师的《es6入门》中的一段话来解释:JS中的同步性与异步性。
/***************************下面是引用*************************************/
JS语言是单线程的单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。于是就有一个概念,任务队列。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
/***************************引用结束*************************************/
所以,异步运行机制如下:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,
就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。
那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步
好了深入理解了ajax的异步性,那么我们现在来解决循环发送ajax的问题。
一 利用for循环发送ajax
var arr = [1,2,3,4,5];
function sendAjax() {
var arrResult = [];
for(var i=0;i<arr.length;i++){
//循环发送ajax
$.ajax({
url : 'ajax.php',
type : 'post',
async : false, //不是异步 记住这里一定是同步的
data : {
"param" : arr[i]
},
success : function(data) {
data = data.trim(); //去掉数据前面的空格
arrResult.push(data);
}
});
}
//循环发送完ajax后做的事情
doSomething(arrResult);
}
//发送完ajax做的事情
function doSomething (data) {
console.log(data);
}
//调用循环发送ajax的方法
sendAjax();
这里面一定需要注意的是需要将async这个属性改为false(异步),如果是true是同步。原生里面是修改open方法里面的第三个参数来控制同步异步,参数同样传一个布尔类型的值。
var xhr = new XMLHttpRequest();
xhr.open('ajax.php','post',false); //同步发送ajax
二 利用递归函数实现
var arr = [1,2,3,4,5];
var arrResult = [];
var index = 0; //计数
function sendAjax() {
//递归跳出的条件
if(index >= arr.length) {
//循环发送完ajax后做的事情
doSomething(arrResult);
return;
}
//循环发送ajax
$.ajax({
url : 'ajax.php',
type : 'post',
async : true, //异步
data : {
"param" : arr[index]
},
success : function(data) {
data = data.trim(); //去掉数据前面的空格
arrResult.push(data);
//每发送一次ajax就计数加1
index++;
//在ajax监听响应完成里面调用递归函数sendAjax()
sendAjax();
}
});
}
//发送完ajax做的事情
function doSomething (data) {
console.log(data);
}
//调用循环发送ajax的方法
sendAjax();
这里的方法是利用递归函数,自己调用自己来完成循环发送ajax的方法。在ajax的事件响应完成函数里面,调用递归函数来继续发送ajax,直到满足条件才跳出递归函数。
具体的实例代码:
本例是先渲染10个标题以及下面的具体内容,需要我们渲染出一个标题就接着渲染内容,如此往复。
//获取数据渲染商品列表以及详情页面
getproductData : function(){
//将baseURL获取到存入到变量中.
var baseURL = this.baseURL;
$.ajax({
url : this.baseURL + 'getcategorytitle',
dataType : 'json',
success : function(obj){
//渲染商品列表 注意返回过来的数据格式
var productListHTML = template('tpl-productList',{list : obj.result});
//商品列表渲染完成后,立即渲染商品详情
$('.briefIn').html(productListHTML);
sendAjaxGetProductDetail();
// 发送ajax请求对应商品的详细数据并渲染
function sendAjaxGetProductDetail () {
var index = 0;
sendAjax();
function sendAjax(){
if(index > 7) {
//加上点击商品头部就显示商品内容,手风琴的效果 单击展开双击收拢
$('.productList').on('click',function(){
//单击展开双击收拢
var productListID = this.dataset.id;
// 手风琴的效果
if( $('.productDetail').eq(productListID).css('display') == 'none') {
$('.productDetail').eq(productListID).css('display','table');
//加上向上的箭头符号
$('.productList').eq(productListID).find('.title').addClass('active');
}else {
$('.productDetail').eq(productListID).css('display','none');
//去掉向上的箭头符号,变成向下的箭头
$('.productList').eq(productListID).find('.title').removeClass('active');
}
});
//超过7就退出递归
return;
}
//获取当前对应的ID号码
var titleid = $('.productList')[index].dataset.id;
//每一次只渲染一个商品详情页面
$.ajax({
url : baseURL + 'getcategory',
dataType : 'json',
async : false,
data : { titleid : titleid },
success :function(obj) {
var target = (obj.result.length%3==0)?obj.result.length/3:Math.ceil(obj.result.length/3);
var OBJ = {
target : target,
result : obj.result,
//判断手风琴效果的ID
indexID : index
}
//console.log(OBJ);
//渲染到页面上
var productDetailHTML = template('tpl-productDetail',OBJ);
$('.productList').eq(index).after(productDetailHTML);
index++;
//利用递归实现
sendAjax();
}
});
}
}
}
});
},
具体的案例代码已上传到:https://github.com/Alex-Li2018/manmanmai-Project