各位 JS 高手你好,问一个关于 for 循环使用$.post 替换全局变量的问题,好像是竞争锁之类的

2024-03-18 10:40:31 +08:00
 abccccabc
各位 JS 高手你好,问一个关于 for 循环使用$.post 替换全局变量的问题,好像是竞争锁之类的。
```
var allimg = 获取到的图片数组;
var oldcontent = 原内容;
for(var i=0; i<allimg.length; i++){
$.post('url', 参数, function(ret) {
if(ret['code'] == 200) {
oldcontent = oldcontent.replace(ret['oldimgurl'], ret['newimgurl']);
}else{
console.log(错误信息);
}
}, 'json');
}
```
这样有一个很大的问题:多个异步去修改同一全局变量,必须要锁定全局变量 oldcontent ,不然只有最后 i 循环 修改 oldcontent 生效。
请高手支招?
7228 次点击
所在节点    JavaScript
83 条回复
sankooc
2024-03-18 13:47:49 +08:00
js 笔试必背八股文
Plumbiu
2024-03-18 13:48:13 +08:00
@qrobot 效果都一样吧,我感觉楼主应该是想将一个文本中的图片地址替换成另外一个地址,这个最开始做不就应该写在循环外?没搞清楚楼主的做法
qrobot
2024-03-18 13:56:22 +08:00
@Plumbiu 他的问题是这个问题

for (var i = 0; i < 100; i += 1) {
setTimeout(() => { console.log(i) }, 0)
}

为什么 console.log 中的 i 不对
qrobot
2024-03-18 13:58:03 +08:00
@qrobot 但是你 debugger 每次调试的时候都是正确的. 运行起来就不对了. 这个问题就有意思了.
qrobot
2024-03-18 14:01:58 +08:00
@Plumbiu 答案我就不公布了, 有兴趣可以自己研究一下为什么
Plumbiu
2024-03-18 14:07:39 +08:00
@qrobot 类似的啊,var 没有块级作用域,你这个代码就会等同于:

```js
var i

// 同步任务
i = 0 -> 定时器等待打印 i
i = 1 -> 定时器等待打印 i
// ...
i = 100

// 异步任务
console.log(i) // i 为 100 ,打印 100 次
```

如果 var 改为 let

```js
// 同步任务
{ let i = 0; } -> 定时器等待打印 i
{ let i = 1; } -> 定时器等待打印 i
// ...
{ let i = 100; }
// 异步任务(在每个块级作用域执行)
{ console.log(i); } // i = 0
{ console.log(i); } // i = 1
```
qrobot
2024-03-18 14:17:20 +08:00
@Plumbiu 那你现在理解吗? 为啥是闭包问题, 你看你的 29l 回答. 其实不是闭包问题, 而是闭包 + 异步回调导致的
qrobot
2024-03-18 14:20:08 +08:00
console.log(1)
setTimeout(() => console.log(2))
console.log(3)

输出结果顺序是 1,3,2 为什么?

你在把这个问题回答上来, 基本上 ecmascript 就没啥大问题了, 其实这两个都是 ecmascript 的基本问题, 一个是异步,一个是闭包
miaotaizi
2024-03-18 14:23:59 +08:00
你把你这段代码理解为 同时发出去 N 个请求, 然后每个请求成功了之后 就立刻去替换了你目标内容

自己想想这回造成什么混乱 不就清楚了吗~
Plumbiu
2024-03-18 14:25:20 +08:00
@qrobot 是有闭包出现,但是主要的问题还是作用域吧,不是闭包出现了问题
R1hu6Hs2sSN8pkVX
2024-03-18 14:29:58 +08:00
没用到 i 不存在 let var 的问题啊。
qrobot
2024-03-18 14:31:40 +08:00
@Plumbiu 异步执行, 完成的结果是具有不确定性的. 数据什么时候返回, 什么时候执行代码, 都是由 post 接口执行的时间来决定的. 本来其实也没什么问题, 但是他又使用了一个闭包, 访问外部的环境, 但是外部的环境会根据返回的结果进行改变.

var i = 0

// 五秒后 i = 1
// 六秒后 i = 2

那么 i = 1, i = 2

如果结果返回的时间变化了


// 五秒后 i = 2
// 六秒后 i = 1

那么 i = 2, i = 1

内部闭包改变了外部变量, 自然不会按照 for 循环的次数进行改变, 而是按照 callback 回调的时间进行改变.

我才说是 闭包 + 异步回调才会有这个问题.
qrobot
2024-03-18 14:34:27 +08:00
```
// 但是他又使用了一个闭包, 访问外部的环境, 但是外部的环境会根据返回的结果进行改变.
oldcontent = oldcontent.replace(ret['oldimgurl'], ret['newimgurl']);
```

这里就是 访问外部的变量, 但是外部的变量会根据返回的结果发生改变. 所以这里获得数据就会产生变化.不会按照逻辑走
Plumbiu
2024-03-18 14:42:25 +08:00
@qrobot 异步执行只有并行情况下结果才不确定吧,楼主都是依次调用 post 接口,按理来说异步队列里面也是依次执行的
Plumbiu
2024-03-18 14:43:17 +08:00
@Plumbiu 如果用了计时器,定的时间不同另算
qrobot
2024-03-18 14:44:22 +08:00
@Plumbiu 你怎么保证 XMLHttpRequest 执行依次调用 post 接口是依次执行的?
qrobot
2024-03-18 14:45:19 +08:00
@Plumbiu 就算不算 XMLHttpRequest, 连 setTimeout 都不保证依次执行
lhstock
2024-03-18 14:48:02 +08:00
我猜啊 有没有一种可能 js 是单线程没有后端预判「锁」的困扰
Plumbiu
2024-03-18 14:48:11 +08:00
@qrobot 哥你看一下 js 中的异步队列
qrobot
2024-03-18 14:49:18 +08:00
@Plumbiu 看了一些 w3c 的规定, 任务队列中规定了执行顺序. 这一点我搞错了, 我一直以为是按照浏览器对任务队列的实现来说, 没有规定任务队列的顺序

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1024619

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX