V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
zhuzhuaini
V2EX  ›  JavaScript

油猴中使用 GM_xmlhttpRequest 的诡异情况

  •  
  •   zhuzhuaini · 2021-06-09 20:31:08 +08:00 · 2326 次点击
    这是一个创建于 1261 天前的主题,其中的信息可能已经有所发展或是发生改变。

    此 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 还是什么别的原因导致的,有大佬知道吗?

    14 条回复    2021-06-10 16:09:36 +08:00
    hgc81538
        1
    hgc81538  
       2021-06-09 20:44:23 +08:00   ❤️ 1
    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);

    }
    wangsongyan
        2
    wangsongyan  
       2021-06-09 20:44:45 +08:00 via iPhone
    onload 是回调函数,异步执行
    zhuzhuaini
        3
    zhuzhuaini  
    OP
       2021-06-09 20:50:51 +08:00
    @hgc81538 用了这个之后,行是行了,但感觉他不是顺序执行啊,输出的是乱序的
    @wangsongyan 那怎么让他 不异步呢。。我就想让他 按顺序 一点点的干活
    zhuzhuaini
        4
    zhuzhuaini  
    OP
       2021-06-09 21:10:51 +08:00
    @hgc81538 虽然顺序是乱的,但是好像没什么问题 能不能讲解下 (function(i)和最后的(i); 是啥作用
    aloxaf
        5
    aloxaf  
       2021-06-09 21:22:25 +08:00
    zhuzhuaini
        6
    zhuzhuaini  
    OP
       2021-06-09 21:33:17 +08:00
    @aloxaf 全英文。。。很不友好....
    OHyn
        7
    OHyn  
       2021-06-09 21:35:03 +08:00
    onload 是回调函数,异步执行,当它执行的时候,for 循环已经结束,i 的值已经是 10.
    你把 var 改成 let 可解决问题。
    这俩东西的作用域有区别。
    zhuzhuaini
        8
    zhuzhuaini  
    OP
       2021-06-09 21:37:42 +08:00
    @OHyn 刚刚试了下 var 改 let 似乎会引起别的问题(不过 onload 倒是没问题了) 我目前用了 L1 的方法 暂时没发现什么 BUG
    knives
        9
    knives  
       2021-06-10 00:04:14 +08:00
    异步转伪同步的话,上 Promise chain 吧,用 await 语法来实现会比较简洁。
    Yvette
        10
    Yvette  
       2021-06-10 04:49:18 +08:00
    需要看中文直接搜闭包也可以
    muzuiget
        11
    muzuiget  
       2021-06-10 08:21:53 +08:00
    老生常谈,GM_xmlhttpRequest 是异步函数, 用 Promise 和 await 解决。
    linyinma
        12
    linyinma  
       2021-06-10 09:27:52 +08:00
    一般浏览器 JS 执行引擎是单线程:“事件驱动”, 调用 GM_xmlhttpRequest 是将消息留到事件队列,因此这个 for 循环只是把事件丢到队列中,等待本事件完成退出以后,JS 引擎线程再从事件队列处理处理刚丢进去的 GM_xmlhttpRequest 事件;
    no1xsyzy
        13
    no1xsyzy  
       2021-06-10 09:30:16 +08:00
    其实你了解下 CPS[1] 也就清楚怎么写了,不过这是个比较抽象的概念,实际工程上也不一定非得理解 CPS
    简单地说,你这样 for(...){xhr({..., onload:x=>...})} 会同时发起十个续延,它们会产生某种竞态导致结果顺序不一致。
    JS 的 var 关键词会传递同一个 ref alias,换 let 就会产生十个不同的 trn alias 了。当然,JS 的 refcap 并没有那么精确。

    [1]: Continuation-Passing Style
    Rhilip
        14
    Rhilip  
       2021-06-10 16:09:36 +08:00
    这不是油猴的问题,是你对 js 异步不了解。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3403 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 04:43 · PVG 12:43 · LAX 20:43 · JFK 23:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.