【问题标题】:Deferred in a while loop在 while 循环中延迟
【发布时间】:2016-07-23 22:25:32
【问题描述】:

所以我想在 jquery 中执行一个延迟的 ajax 请求,直到我收到特定的服务器响应(非空)。我该怎么做呢?

while (data.response != null) {
  $.ajax(..).done(function(data);
}

function doUntilResult() {
    Server.doA().done(function(data) {
      Server.doB(data).done(function(result) {
          //if result == null, repeat again
      });
    });
}

【问题讨论】:

    标签: jquery ajax asynchronous deferred


    【解决方案1】:

    你不能在 Javascript 中这样循环。 Javascript 是一个事件驱动的系统。当您像这样循环时,将无法处理任何其他事件。因此,甚至您的第一个 Ajax 调用都不会得到处理。事实上,当客户端尝试同时进行数百万次 ajax 调用时,您可能会用尽一些资源来驱动 JS 引擎。

    相反,您可以异步使用 ajax 结果,然后根据结果决定是否再次调用它:

    function poll() {
        $.ajax(...).then(function(data) {
            if (!data.xxxx) {
                // call it again after some short delay
                setTimeout(poll, 1000);
            } else {
                // got the result we wanted, process it
            }
        })
    }
    
    // start the polling
    poll();
    

    如果你想让整个事情都返回一个承诺,你可以这样做:

    function delay(t) {
        return new $.Deferred(function(def) {
            setTimeout(def.resolve, t);
        }).promise();
    }
    
    function poll() {
        return $.ajax(...).then(function(data) {
            if (!data.xxxx) {
                // call it again after some short delay
                return delay(1000).then(poll);
            } else {
                // got the result we wanted, process it
                return someValue;
            }
        })
    }
    
    // start the polling
    poll().then(function(result) {
        // process result here
    });
    

    附:尽可能快地不断地轮询某些服务器几乎从来都不是正确的事情。如果您只有几个用户,这可能工作得很好,但是一旦您有很多用户,所有这将做的就是用空的轮询请求压倒您的服务器,在大多数情况下,没有什么可以返回给客户端。这是处理负载的噩梦。找到不轮询的架构会更好,但如果你要轮询,那么至少使用某种计时器在未来某个时间轮询,而不是尽可能快。

    P.P.S.如果您真的只是在等待服务器上的某些值发生变化,那么也许您应该使用 webSocket 连接。这样,客户端建立与服务器的连接并且连接持续存在,然后在未来的任何时候,服务器都可以简单地向客户端发送消息。客户端根本不需要轮询。这可以大大提高您的服务器基础架构的效率,并且可以提供更及时的结果。

    【讨论】:

    • 我想在这次投票中使用延期,这可能吗?这样我就可以做 poll().done(...) 当然只会在 !data.xxx 时调用?
    • @Wesley - 我不明白你想要什么。我的答案中有$.ajax(...).then(),它已经使用了一个承诺。我更喜欢使用 ES6 标准 .then() 而不是 jQuery 特定的 .done()。如果您希望在轮询完成后整个事情都返回一个承诺,那可以做到(尽管这不是您在问题中要求的)。
    • @Wesley - 我又添加了一个选项。
    • 也不明白,你在回调中使用了return,那是怎么回事?我只使用 .done ,你总是必须用返回值来解决或拒绝。
    • 最后一个问题,我上面的代码不是很详细,我基本上在那个“循环”中做了两个请求,见编辑过的代码;那么它将如何工作,因为我正在处理多个延期。
    【解决方案2】:

    要进行大量控制,请写doUntilResult() 接受:

    • doThis 回调函数,用于确定要执行的操作以及重试/终止条件。
    • 一个整数,用于确定调用之间的延迟。
    function doUntilResult(doThis, t) {
        function delay() {
            return $.Deferred(function(dfrd) {
                window.setTimeout(dfrd.resolve, t);
            });
        }
        function poll() {
            return $.when(doThis()).then(function(data) { // $.when() caters for non-async functions to be passed.
                return (data === null) ? delay().then(poll) : data; // continue on null, otherwise return data.
            });
        }
        return poll();
    }
    

    现在,您有了一个通用的doUntilResult() 函数,可以在不重写的情况下改变您对“完成直到”的事情的想法。一切都在调用(或调用)中指定。

    对于原始问题,请按以下方式调用:

    doUntilResult(function() {
        return $.ajax(..).then(function(data) {
            return data || null; // adjust as required to ensure that `null` is returned only for the "continue" condition.
        });
    }, 1000); // 1 second delay between calls
    

    对于已编辑的问题,请按以下方式调用:

    doUntilResult(function() {
        return Server.doA().then(function(data) {
            return Server.doB(data);
        }).then(function(result) {
            return result || null; // adjust as required ...
        });
    }, 1000); // 1 second delay between calls
    

    无论您想做什么,请始终确保回调的 .then 链终止于一个函数,该函数在轮询继续时返回 null

    Here's a simple demo 生成一个 0-10 的随机数; 9 或更大的数字被视为数据;低于 9 会导致重试。 (在控制台中监控进度)。

    【讨论】:

      猜你喜欢
      • 2013-01-24
      • 2018-04-05
      • 1970-01-01
      • 2014-10-21
      • 1970-01-01
      • 1970-01-01
      • 2020-02-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多