背景
最近接手了个老项目,主要用到的技术栈有点老: RequireJS
+ jQuery
。在实现 jQuery Ajax
并发请求数据中遇到了些问题,顺便做下记录。
实现内容
通过 jQuery Ajax
并发请求数据,要保证并发的接口都完成请求(成功或者失败)才能进行下一步。
第一版代码
var cacheAvatar = {}
var completeTag = 0;
for (var j; j < uidList.length; j++) {
$.ajax({
url: `//www.baidu.com/u/xxxx/${uidList[j]}`,
type: 'get',
dataType: 'json',
cache: false,
success: function(res){
...
// 赋值
cacheAvatar[uidList[j]] = res.data.avatar
},
error: function(){
...
},
complete: function(){
completeTag++
if (completeTag === uidList.length) {
// 完成所有请求
}
}
})
}
第一版代码执行下来,发现接口 url
的uidList[j]
是对的,而 cacheAvatar[uidList[j]]
的 key
却只有一个,并是 uidList
最后一个 item
。想了下 var
没有块级作用域的问题,那就用老的方法:闭包作用域。
第二版代码
var cacheAvatar = {}
var completeTag = 0;
for (var j; j < uidList.length; j++) {
(function(j){
$.ajax({
url: `//www.baidu.com/u/xxxx/${uidList[j]}`,
type: 'get',
dataType: 'json',
cache: false,
success: function(res){
...
// 赋值
cacheAvatar[uidList[j]] = res.data.avatar
},
error: function(){
...
},
complete: function(){
completeTag++
if (completeTag === uidList.length) {
// 完成所有请求
}
}
})
})(j)
}
第二版代码执行下来, cacheAvatar[uidList[j]]
赋值正常了,但还有别的问题:completeTag
的值多次回调触发都是 1
, 没按我们预期的自加,影响了 是否所有请求
的判断。
那如果我们把 completeTag
的值也传入闭包中呢?
答案还是不行的,因为 completeTag
的值是 基本类型
, 这时 completeTag
也就是 按值传递
, 闭包内部各自拿到只是实参的副本,同时保存在各自的上下文中,对副本修改并不会影响变量 completeTag
自身,所以多个闭包里 completeTag
多次自加只会等于 1
。
既然知道了原因,那我们就用 按引用传递
的方式来解决,改变 completeTag
值的类型,通过传递对象的引用,实现多个闭包里实际上是对同一个对象的读写。
第三版代码
var cacheAvatar = {}
var completeTag = [];
for (var j; j < uidList.length; j++) {
(function(j){
$.ajax({
url: `//www.baidu.com/u/xxxx/${uidList[j]}`,
type: 'get',
dataType: 'json',
cache: false,
success: function(res){
...
// 赋值
cacheAvatar[uidList[j]] = res.data.avatar
},
error: function(){
...
},
complete: function(){
completeTag.push(j)
if (completeTag.length === uidList.length) {
// 完成所有请求
}
}
})
})(j)
}
将 completeTag
变量类型改为 引用类型
,这样传入闭包时内部拿到的实际上是completeTag
内存地址引用,这样代码如我们预期所执行了。
总结
所谓 ”万变不离其宗“,不管技术栈多老或者五花八门迭代更新,还是不要忽略了基础的学习。