循环发送AJAX的方法

我在之前讲过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个标题以及下面的具体内容,需要我们渲染出一个标题就接着渲染内容,如此往复。
循环发送AJAX的方法

//获取数据渲染商品列表以及详情页面
	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