技术解析

油猴中使用 GM_xmlhttpRequest 的诡异情况
0
2021-08-10 02:58:03
idczone

此 BUG 复现率百分之一百

代码如下:

for(var i=0;i<10;i++){

GM_xmlhttpRequest({

                          url: "http://www.qq.com/",
                          
                          responseType: "document",
                          
                          headers:{'overrideMimeType': 'text/html;charset=gb2312'},
                          
                          onload:
                          
                      function(res){
                      
                          console.log(i)
                          
                          },
                          
                });}
                

我在油猴中使用此代码,但是输出并不是理想情况:

按代码执行来说 应该进入 For 循环后 访问 qq,获取到 QQ 的源代码然后打印当前循环次数,然后自增 i,进入下一次循环

理想情况打印的内容应该为 0-1-2....-9

但事实却是只会输出 10 个 10. 如图: https://www.hualigs.cn/image/60c0b3a27dd2c.jpg

而我如果稍加修改 将输出语句写在 GM_xmlhttpRequest 外面,for 循环的里面,则没有问题,如图: https://www.hualigs.cn/image/60c0b4086faa3.jpg

所以我认为就是 GM_xmlhttprequest 的问题,但是我不清楚这是 bug 还是什么别的原因导致的,有大佬知道吗?


for(var i=0;i<10;i++){

(function(i){

GM_xmlhttpRequest({
url: "http://www.qq.com/",
responseType: "document",
headers:{'overrideMimeType': 'text/html;charset=gb2312'},
onload: function(res){
console.log(i)
},
});

})(i);
}

onload 是回调函数,异步执行

用了这个之后,行是行了,但感觉他不是顺序执行啊,输出的是乱序的
那怎么让他 不异步呢。。我就想让他 按顺序 一点点的干活

虽然顺序是乱的,但是好像没什么问题 能不能讲解下 (function(i)和最后的(i); 是啥作用

老生常谈的问题了: https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example

全英文。。。很不友好....

onload 是回调函数,异步执行,当它执行的时候,for 循环已经结束,i 的值已经是 10.
你把 var 改成 let 可解决问题。
这俩东西的作用域有区别。

刚刚试了下 var 改 let 似乎会引起别的问题(不过 onload 倒是没问题了) 我目前用了 L1 的方法 暂时没发现什么 BUG

异步转伪同步的话,上 Promise chain 吧,用 await 语法来实现会比较简洁。

需要看中文直接搜闭包也可以

老生常谈,GM_xmlhttpRequest 是异步函数, 用 Promise 和 await 解决。

一般浏览器 JS 执行引擎是单线程:“事件驱动”, 调用 GM_xmlhttpRequest 是将消息留到事件队列,因此这个 for 循环只是把事件丢到队列中,等待本事件完成退出以后,JS 引擎线程再从事件队列处理处理刚丢进去的 GM_xmlhttpRequest 事件;

其实你了解下 CPS[1] 也就清楚怎么写了,不过这是个比较抽象的概念,实际工程上也不一定非得理解 CPS
简单地说,你这样 for(...){xhr({..., onload:x=>...})} 会同时发起十个续延,它们会产生某种竞态导致结果顺序不一致。
JS 的 var 关键词会传递同一个 ref alias,换 let 就会产生十个不同的 trn alias 了。当然,JS 的 refcap 并没有那么精确。
[1]: Continuation-Passing Style

这不是油猴的问题,是你对 js 异步不了解。

数据地带为您的网站提供全球顶级IDC资源
在线咨询
专属客服