如何在fetch的then函数中访问对应请求对象解决异步循环问题?
解决方案
这个问题本质是异步回调和循环变量的作用域冲突导致的——var声明的变量是函数级作用域,循环里的所有迭代共享同一个变量引用,当异步回调执行时,循环已经走完了,变量早就被替换成最后一个元素的值了。给你几个实用的解决办法:
1. 用let/const替换所有var
这是最简单的方案,let和const是块级作用域,每个循环迭代都会创建独立的变量绑定,异步回调里访问的就是当前迭代的对象:
// 把循环里的var全换成let/const for (let i = 0; i < expectedRows; i++) { let row = myArray[i]; let customerCode = row['customer_code']; let customerName = row['customer_name']; let customerBalance = row['customer_balance']; let callBody = { user: 'USER', code: customerCode, name: customerName, balance: customerBalance }; let fetchOptions = { method: "POST", cache: "no-cache", headers: { "Content-Type": "application/x-www-form-urlencoded", }, redirect: "error", referrer: "ux-import", body: JSON.stringify(callBody), }; const epurl = baseEP + '/customer/create'; fetch(epurl, fetchOptions) .then(response => response.json()) .then(response => { if (response.error === 0) { console.log('insert ok'); insertRows++; } else if (response.error == 2) { console.log('insert error => update'); const updateEP = baseEP + '/customer/update'; // 这里的fetchOptions就是当前迭代的对象,不会被覆盖 fetch(updateEP, fetchOptions) .then(updResponse => updResponse.json()) .then(updResponse => { if (updResponse.error === 0) { console.log('update ok.') updateRows++; } else { console.log('update error: ' + updResponse.msg) errorMessages.push(updResponse.msg); } }) .catch(error => { console.log('update failure'); errorMessages.push(error); }); } else { console.log('insert error.'); errorMessages.push(response.msg); } }) .catch(error => { console.log('insert failure.'); errorMessages.push(error); }); }
2. 用forEach遍历数组
forEach的回调函数本身就会创建独立作用域,每个迭代的变量都是独立的,天然避免变量共享问题,代码也更简洁:
myArray.forEach(row => { const customerCode = row['customer_code']; const customerName = row['customer_name']; const customerBalance = row['customer_balance']; const callBody = { user: 'USER', code: customerCode, name: customerName, balance: customerBalance }; const fetchOptions = { method: "POST", cache: "no-cache", headers: { "Content-Type": "application/x-www-form-urlencoded", }, redirect: "error", referrer: "ux-import", body: JSON.stringify(callBody), }; const epurl = baseEP + '/customer/create'; fetch(epurl, fetchOptions) .then(response => response.json()) .then(response => { // 逻辑和之前一致,这里的fetchOptions完全属于当前迭代 if (response.error === 0) { console.log('insert ok'); insertRows++; } else if (response.error == 2) { console.log('insert error => update'); const updateEP = baseEP + '/customer/update'; fetch(updateEP, fetchOptions) .then(updResponse => updResponse.json()) .then(updResponse => { if (updResponse.error === 0) { console.log('update ok.') updateRows++; } else { console.log('update error: ' + updResponse.msg) errorMessages.push(updResponse.msg); } }) .catch(error => { console.log('update failure'); errorMessages.push(error); }); } else { console.log('insert error.'); errorMessages.push(response.msg); } }) .catch(error => { console.log('insert failure.'); errorMessages.push(error); }); });
3. 用IIFE包裹迭代逻辑(兼容老环境)
如果需要兼容不支持ES6+的老浏览器,可以用立即执行函数表达式(IIFE)把每个迭代的变量封闭起来,形成独立作用域:
for (var i = 0; i < expectedRows; i++) { // 把当前迭代的row传入IIFE,形成独立作用域 (function(currentRow) { var customerCode = currentRow['customer_code']; var customerName = currentRow['customer_name']; var customerBalance = currentRow['customer_balance']; var callBody = { user: 'USER', code: customerCode, name: customerName, balance: customerBalance }; var fetchOptions = { method: "POST", cache: "no-cache", headers: { "Content-Type": "application/x-www-form-urlencoded", }, redirect: "error", referrer: "ux-import", body: JSON.stringify(callBody), }; var epurl = baseEP + '/customer/create'; fetch(epurl, fetchOptions) .then(response => response.json()) .then(response => { // 这里的fetchOptions是当前IIFE内的变量,不会被后续循环覆盖 if (response.error == 2) { const updateEP = baseEP + '/customer/update'; fetch(updateEP, fetchOptions) .then(updResponse => updResponse.json()) .then(updResponse => { if (updResponse.error === 0) { console.log('update ok.') updateRows++; } else { console.log('update error: ' + updResponse.msg) errorMessages.push(updResponse.msg); } }) .catch(error => { console.log('update failure'); errorMessages.push(error); }); } else if (response.error === 0) { console.log('insert ok'); insertRows++; } else { console.log('insert error.'); errorMessages.push(response.msg); } }) .catch(error => { console.log('insert failure.'); errorMessages.push(error); }); })(myArray[i]); }
关于this的补充说明
你在then回调里用this指向window,是因为默认情况下普通函数的this在非严格模式下会绑定到window。其实根本不需要依赖this来获取请求对象——通过上面的作用域绑定,我们已经能直接访问到当前迭代的fetchOptions和callBody,直接用这些变量就可以实现你想要的逻辑,比如你期望的this.request.body,直接用callBody或者fetchOptions.body就行。
内容的提问来源于stack exchange,提问作者Edo Elgx
相关产品推荐
相关产品推荐

