背景
最近接手了个老项目,主要用到的技术栈有点老: 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内存地址引用,这样代码如我们预期所执行了。
总结
所谓 ”万变不离其宗“,不管技术栈多老或者五花八门迭代更新,还是不要忽略了基础的学习。