循环发起多异步请求时数据重复问题求助
你猜的完全没错!这就是JavaScript对象的引用特性加上异步操作的延迟执行共同导致的坑。
问题根源拆解
你在循环里反复传入同一个item对象——JS里对象是按引用传递的,也就是说所有循环里的item指向的都是内存里同一个对象。而CaseProvider.create()是异步操作,它不会立刻发起请求,得等整个for循环(同步代码)全部执行完之后,才会依次执行这些异步任务。这时候item的title已经被最后一次循环(i=4)改成了"Hello 4",所以所有请求自然都带着这个最终值发送了。
至于console.log(item.title)看起来正常,是因为它是同步执行的,每次循环修改完title就立刻输出,赶在对象被下一次修改前就打印出来了,这只是个“即时快照”,不代表异步请求执行时对象的状态~
解决方案:每次循环创建独立的对象副本
要解决这个问题,核心就是让每个异步请求携带独立的对象,而不是共用同一个引用。这里有两种简单的实现方式:
方式1:在函数内部创建副本
修改createNewCase函数,每次都基于传入的item创建一个新对象,再修改title:
function createNewCase(number, item) { // 用扩展运算符创建原对象的浅拷贝 const newItem = {...item}; newItem.title = vm.title + ' ' + number; console.log(newItem.title); CaseProvider.create(newItem).then(function (case) { console.log(case.id); }); }
如果你的项目环境不支持ES6扩展运算符,也可以用Object.assign替代:
const newItem = Object.assign({}, item);
方式2:在循环内提前创建副本
也可以把创建副本的步骤放到循环里,确保每次传给createNewCase的都是新对象:
vm.title = vm.case.title; for (let i = 1; i <= vm.number_of_cases_to_create; i++) { // 每次循环生成新的对象副本 const newCase = {...vm.case}; vm.createNewCase(i, newCase); } function createNewCase(number, item) { item.title = vm.title + ' ' + number; console.log(item.title); CaseProvider.create(item).then(function (case) { console.log(case.id); }); }
原理说明
不管哪种方式,本质都是每次循环都生成一个全新的对象,它们在内存里有独立的存储空间。后续修改title只会影响当前这个新对象,不会覆盖之前的。当异步请求最终执行时,每个请求携带的都是各自独立的对象,自然就能得到"Hello 1"到"Hello 4"的正确结果了。
内容的提问来源于stack exchange,提问作者Cudos

