Ajax的异步与同步

Posted by Eric on June 9, 2020

今天在写前端脚本时遇到个关于ajax请求的异步与同步的问题。

目的

数组A记录了要访问的URL,for循环遍历出URL后使用ajax请求后端,再根据当前的index在数组B获取相应的值传递给下一个参数。

问题

程序结构是没有问题的,然而在switch的时候一直蹦到了default,调试发现index一直输出结果为4,四次的index值都是4。

程序结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
...
...

//initialize
var cpldict
var ppldict
var ybddict
var atpldict

var jsonDict = ["url1","url2","url3","url4"]

for(var index = 0;index < jsonDict.length;index++){

            $.getJSON("data/"+jsonDict[index],
                function (data, textStatus, jqXHR) {
                console.log(index);
              	switch(index){
                        case 0:
                            cpldict = JSON.stringify(data);
                            break;
                        case 1:
                            ppldict = JSON.stringify(data);
                            break;
                        case 2:
                            ybdict = JSON.stringify(data);
                            break;
                        case 3:
                            atpldict = JSON.stringify(data);
                            break;
                        default:
                            alert()

                    }
              ...
              ...
              ...
              
                }
            );
 }
1
2
3
4
5
6
7
8
9
10
//output

4
[alert]
4
[alert]
4
[alert]
4
[alert]

原因

后来突然明白,这里出现问题是因为没有处理好异步与同步

1
2
3
4
5
6
7
	getJSON: function( url, data, callback ) {
		return jQuery.get( url, data, callback, "json" );
	},

	getScript: function( url, callback ) {
		return jQuery.get( url, undefined, callback, "script" );
	}

问题的本质出在$.getJSON()上,我以为它是XMLHttpRequest()方法的一种引用调用,实际上还是GET方法(不然为什么叫getJSON),在jQuery中GET方法使用了ajax,异步加载创建线程,循环继续进行。线程内程序执行时循环已经跳出(i=4),所以console一直显示4

Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术。

Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式、快速动态网页应用的网页开发技术,无需重新加载整个网页的情况下,能够更新部分网页的技术。

通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

解决方法

ajax异步转同步

默认情况下,ajax都是异步执行。使用简写($.get(),$.post(),$.getJSON()等等)无法设置参数,因此回归最原始的ajax形态,如下:

1
2
3
4
5
6
7
8
9
10
11
12
$.ajax({
	url:"",
	type: "POST/GET",
	data: {
		
	},
  // async设置为true(默认)为异步,false为同步
	async: false,
  //success是之前的写法,现在推荐用.done()
	}).done( function( data ) {

});

欲执行完此ajax请求再执行下一个ajax请求,加上async: false即可

传参:创建子方法

问题的根本其实并不是在于异步/同步执行,而是留不住那个index参数。只要能留住index异步其实也没问题,于是乎:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
...
...
...

for(var index = 0;index < jsonDict.length;index++){

  getJSONtext("data/"+jsonDict[index],index);
};

function getJSONtext(url,j){
  $.getJSON(url,function (data, textStatus, jqXHR) {
    console.log(j);
    switch(j){
      case 0:
        cpldict = JSON.stringify(data);
        break;
      case 1:
        ppldict = JSON.stringify(data);
        break;
      case 2:
        ybdict = JSON.stringify(data);
        break;
      case 3:
        atpldict = JSON.stringify(data);
        break;
      default:
        alert()
    }
  }
}

...
...
...

不可取的方法

还有几种方法,但最终验证是不可行的。我们分析一下为什么这些方法是不可取的,对异步有更深的了解

新设定一个变量,每当完成以后自加一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
...
...
...

var newIndex = 0;


for(var index = 0;index < jsonDict.length;index++){

            $.getJSON("data/"+jsonDict[index],
                function (data, textStatus, jqXHR) {
                console.log(newIndex);
              	switch(newIndex){
                        case 0:
                            cpldict = JSON.stringify(data);
                            break;
                        case 1:
                            ppldict = JSON.stringify(data);
                            break;
                        case 2:
                            ybdict = JSON.stringify(data);
                            break;
                        case 3:
                            atpldict = JSON.stringify(data);
                            break;
                        default:
                            alert()

                }
              	newIndex++;
              
              ...
              ...
              ...
              
                }
            );
 }
1
2
3
4
5
6
//output

1
2
3
4

看起来output已经是按照顺序依次读取,但实际上很有可能原本应赋值给cpldictdata,赋值给了其他对象。

这里会有一点绕。我们先来看看,同步执行的程序逻辑

exec-sync

GET发送A0至服务器,返回data0,再将data0赋值给B0

这是正确的。

接下来,倘若按照上面所述引入一个newIndex

exec-async-1

在第一个循环中,为了获取data0并赋值给B0ajax将创建一个线程event a0(),先用A0找到data0,然后再去找B0

创建线程后,不论a0()是否执行完毕(找到了data0B0),程序(图中框内)将继续进行,依次创建a1(),a2(),a3(),a4()

a1(),a2()这些线程好比于田径场上的运动员,创建就代表可以开始跑步了。a0()是第一个起跑的,但不一定是第一个到,第一名(B0)不一定属于a0()异步执行中,无法保证先创建的线程先response,这也不符合异步的目的。

exec-async-2

假如a0()在跑步(请求data0)的时候「摔跤」了,结果a1()先抵达终点,那么B0就会赋值给A1,接下来就不说了,只要有一组数据错了,其他的必然出错。

结论

借着一个error梳理了ajax异步加载`的原理,下次别再犯这种低级错误了。

Ajax-principle