在 while 循环中延迟

Posted

技术标签:

【中文标题】在 while 循环中延迟【英文标题】:Deferred in a while loop 【发布时间】: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
      );
    );

【问题讨论】:

【参考方案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 会导致重试。 (在控制台中监控进度)。

【讨论】:

以上是关于在 while 循环中延迟的主要内容,如果未能解决你的问题,请参考以下文章

添加一个没有 Thread.sleep 的延迟和一个啥都不做的 while 循环

xml 延迟加载带有while循环的sfnt滑块

xml 延迟加载带有while循环的sfnt滑块

如何消除while循环命令的延迟?

如何在 JavaScript 循环中添加延迟?

无限循环 - 延迟 - 单独的线程