今天在写前端脚本时遇到个关于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已经是按照顺序依次读取,但实际上很有可能原本应赋值给cpldict
的data
,赋值给了其他对象。
这里会有一点绕。我们先来看看,同步执行的程序逻辑
GET
发送A0
至服务器,返回data0
,再将data0
赋值给B0
。
这是正确的。
接下来,倘若按照上面所述引入一个newIndex
:
在第一个循环中,为了获取data0
并赋值给B0
,ajax
将创建一个线程event a0()
,先用A0
找到data0
,然后再去找B0
。
创建线程后,不论a0()
是否执行完毕(找到了data0
和B0
),程序(图中框内)将继续进行,依次创建a1()
,a2()
,a3()
,a4()
。
a1()
,a2()
这些线程好比于田径场上的运动员,创建
就代表可以开始跑步了。a0()
是第一个起跑的,但不一定是第一个到,第一名(B0
)不一定属于a0()
。异步执行中,无法保证先创建的线程先response,这也不符合异步的目的。
假如a0()
在跑步(请求data0
)的时候「摔跤」了,结果a1()
先抵达终点,那么B0
就会赋值给A1
,接下来就不说了,只要有一组数据错了,其他的必然出错。
结论
借着一个error梳理了ajax异步加载`的原理,下次别再犯这种低级错误了。