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

JS 如何实现无限次数的顺序请求?

  •  1
     
  •   abcbuzhiming · 2017-08-28 10:41:38 +08:00 · 4089 次点击
    这是一个创建于 2425 天前的主题,其中的信息可能已经有所发展或是发生改变。
    有个功能叫长轮询,很多时候用于要求不特别高的 web 消息推送或者 webim 聊天。基本原理是客户端向服务器发起一次请求后,该请求就被服务器 hold 住 n 秒(从 30-90s 不等),此时如果服务器发现有属于这个连接的新消息,就返回消息,然后本次请求结束,客户端再次发起下一次请求,无限循环。

    这里面有两个难点:
    请求是顺序的,一个请求结束后才发起另外一个,也就是说是链式请求,而大部分的客户端异步请求是不会等待一个完结才开始另外一个的。
    请求的数目是无限的,不是有限的。类似 Porimse 这样的链式请求的实现从方式上看其实是先把有限个请求任务全部串起来,然后再让它们执行。而我这里,请求数目理论上是无限的,所以不能预先串好

    有个叫 sockjs 的库在低版本 IE 上就能实现这样“无限链式请求”功能,我看了很久代码,还是没搞懂原理,请指点
    29 条回复    2017-08-29 19:52:47 +08:00
    alber1986
        1
    alber1986  
       2017-08-28 10:44:13 +08:00
    我也想知道
    biuuu
        2
    biuuu  
       2017-08-28 10:52:15 +08:00
    用递归啊
    Arrowing
        3
    Arrowing  
       2017-08-28 10:56:05 +08:00
    在客户端的 ajax 代码里,success 和 error 里发出下一次请求,难道这样不可以吗?
    chenyu8674
        4
    chenyu8674  
       2017-08-28 10:59:37 +08:00
    没太理解 LZ 的意思,不过从描述来看不难实现啊
    1. 从前一个请求的 onResponse 里触发下一个请求
    2. 维护一个请求任务列表按顺序调用
    abcbuzhiming
        5
    abcbuzhiming  
    OP
       2017-08-28 10:59:54 +08:00
    @biuuu 逻辑设计上讲,用递归是不合理的,递归从原理上讲必须有出口,也就是递归深度是事先设定好的,有穷的,虽然我这个案例里 JS 的请求从实际上讲,必然会有退出的可能性(比如出错),但是从场景上来讲,这是一个无限循环,所以我觉得递归没法用在这里,难道你不怕栈溢出?还是你说的递归和我想的不是一个东西?
    ETiV
        6
    ETiV  
       2017-08-28 11:01:45 +08:00 via iPhone   ❤️ 1
    async.queue

    并发设置好,直接 push 相应数量的回调函数;每一次请求收到响应后再 push 一次,就可以车轮一样,无限次了……

    另外你要是做聊天,不要「一个请求结束后才发起另外一个」,相邻两次连接中间的空档期会丢消息的。
    wunonglin
        7
    wunonglin  
       2017-08-28 11:02:17 +08:00
    怎么不用 WebSocket ?
    abcbuzhiming
        8
    abcbuzhiming  
    OP
       2017-08-28 11:12:09 +08:00
    @chenyu8674
    1 方法这算回调吧
    2.方法是啥意思,能说明白点不
    abcbuzhiming
        9
    abcbuzhiming  
    OP
       2017-08-28 11:13:08 +08:00
    @ETiV 你这是客户端的东西?我放狗半天没搜到,我现在要解决的是客户端无限轮询问题,服务端不需要担心,不会丢消息的
    abcbuzhiming
        10
    abcbuzhiming  
    OP
       2017-08-28 11:13:21 +08:00
    @wunonglin IE8,没法用
    hxsf
        11
    hxsf  
       2017-08-28 11:15:31 +08:00
    function fetch_once () {
    fetch(......, function(err, data) {
    // your codes here
    setTimeout(fetch_once, 1000 /* delay */ ) // or use `nextTick`
    })
    }
    ETiV
        12
    ETiV  
       2017-08-28 11:17:54 +08:00 via iPhone
    @abcbuzhiming
    搜这个 site:npmjs.com async
    里面有个.queue 方法
    flowfire
        13
    flowfire  
       2017-08-28 11:18:54 +08:00
    let getMessage = cb => {
    // xhr 操作
    xhr.onreadyStateChange = () => {
    // if(XXX) return;
    let res = xhr.responseText;
    if (res.success) {
    // 操作数据
    }
    cb();
    }
    }

    ........我还是没懂难度在哪里
    flowfire
        14
    flowfire  
       2017-08-28 11:21:54 +08:00
    如果可以用 async 那就更简单了
    let do = async () => {

    while(true){
    await new Promise((res,rej)=>{
    // xhr 操作
    // 收到数据后 res();
    });
    }

    }

    do();
    flowfire
        15
    flowfire  
       2017-08-28 11:24:34 +08:00
    @flowfire #14 我忘记 do 是关键字了。。。。
    flowfire
        16
    flowfire  
       2017-08-28 11:28:44 +08:00
    abcbuzhiming
        17
    abcbuzhiming  
    OP
       2017-08-28 11:30:09 +08:00
    谢谢各位,我想我明白咋回事了,因为最近写 Promise,我的思路走死胡同了,老是在想串链任务,其实就像
    @chenyu8674 @flowfire 说的,回调里调用自身就能解决
    iskyzh
        18
    iskyzh  
       2017-08-28 11:34:45 +08:00 via Android
    @abcbuzhiming 个人认为 JavaScript 里的回调不能等同于递归。诸如 function a() { a(); } 必然是会栈溢出的。但是回调不应该这么理解,因为触发事件执行回调的时候,入口函数应该是「被触发的事件」,而非「注册回调的地方」。因此在 onsuccess/onerror 里再去发 ajax 请求是可行的,而且不会导致爆栈。
    t123yh
        19
    t123yh  
       2017-08-28 14:12:05 +08:00 via Android
    给楼主指条明路:socket.io 向下支持到 IE6
    mooncakejs
        20
    mooncakejs  
       2017-08-28 14:19:33 +08:00
    const loop = async ()=>{
    while(true){
    await ping()
    await sleep(10000)
    }
    }
    Immortal
        21
    Immortal  
       2017-08-28 15:17:46 +08:00
    @t123yh socketio 对于低版本的浏览器 因该也是走的轮询
    t123yh
        22
    t123yh  
       2017-08-28 15:24:31 +08:00 via Android
    @Immortal 这样也很好啊,用轮子
    fulvaz
        23
    fulvaz  
       2017-08-28 16:30:31 +08:00
    @iskyzh 我记得 v8 可以尾递归了, 只要写的时候注意, 不会有溢出的问题
    xieranmaya
        24
    xieranmaya  
       2017-08-28 17:30:37 +08:00
    异步递归啊:
    ```js
    (function r() {
    fetch(url).then(r)
    }())
    ```
    lamada
        25
    lamada  
       2017-08-28 23:19:35 +08:00 via Android
    eventstream
    ryd994
        26
    ryd994  
       2017-08-29 08:07:21 +08:00
    @abcbuzhiming 有尾递归优化的情况下,递归等效于循环
    这也是为什么对函数式语言尾递归优化这么重要的原因:因为函数式语言的循环一般就是递归实现的
    abcbuzhiming
        27
    abcbuzhiming  
    OP
       2017-08-29 09:25:48 +08:00
    @ryd994 想请教一个问题,递归不都应该有出口吗?如果是无限循环比如 while(true),也能用尾递归等效?这什么原理
    ryd994
        28
    ryd994  
       2017-08-29 11:16:44 +08:00 via Android
    @abcbuzhiming 没有尾递归优化的话,调用栈会一直往上堆,直到爆掉。所以在大多数过程式语言里,一般不建议递归太多层,而且会很小心返回条件。
    有尾递归优化的话,尾调用只是等于赋值然后跳转被调用函数,不需要保留调用函数的内容。正确使用的话不会爆栈。
    函数式编程只考虑基本元素,不考虑运行环境的高阶函数的话,循环只能用递归表示。
    xieranmaya
        29
    xieranmaya  
       2017-08-29 19:52:47 +08:00
    再贴一个写法:
    (async function r() {
    await fetch(url)
    r()
    }())

    楼主你要知道,异步递归不是真的递归,是不会产生调用栈的
    所以对于异步代码分析什么尾递归,调用栈都基本没什么意义
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2862 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 00:28 · PVG 08:28 · LAX 17:28 · JFK 20:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.