2012-03-28 134 views
1

如果我以一种“奇怪”的方式做事,我想以道歉形式作为序言,因为我主要是一名C开发人员,并且我正在以这种方式解决AJAX问题C.JavaScript同步Ajax请求Idiosyncrasies

我有一个脚本,它将连接到一个“推送服务器”,该服务器等待一条消息可用,然后只发送一条消息并断开连接。客户端必须重新建立连接以侦听未来的消息。

我试图通过实施异步回调中的同步 AJAX调用做到这一点,和它的作品,除了它出现在DOM(也许?我展示我的JS在这里的无知)将阻塞,直到所有通话已完成。

我不知道如何用纯异步调用来做到这一点,因为我不希望每次都有一个回调调用回调来耗尽堆栈。

这是代码:

 $.ajax({ 
      url: './recoverDevice', 
      data: JSON.stringify(requestData), 
      dataType: 'json', 
      type: 'POST', 
      success: function(j) 
      { 
       console.log(j); 
       if (j.success) 
       { 
        //Indefinitely listen for push messages from the server 

        var loopMore = true; 
        while(loopMore) 
        { 
         $.ajax({ 
          async: false, 
          url: './getPendingMessage', 
          dataType: 'json', 
          type: 'POST', 
          success: function(j) 
          { 
           //alert(j.message); 
           $("#progressBox").append("<li>" + j.message + "</li>"); 
           loopMore = !j.complete; 
          } 
         }); 
        } 

       } 
       else 
       { 
        $("#errorBox").show(); 
        $("#errorBox").text(j.errorMessage); 
       } 
      } 
     }); 

现在,从逻辑上讲,这个代码应该工作。在一个异步函数中,我遍历了一个同步 JS调用,每当我收到一条消息时,我都会将它附加到DOM,并且只有当服务器告诉我将不会有更多消息时,才会退出循环,结束异步线程并完成任务。

问题是,一旦收到所有消息,DOM访问似乎全部合并。即一旦所有消息已被接收并且异步线程退出,附件才会发生。

注释掉alert是一个测试 - 它的工作原理完美。每次通知后我都会收到一个消息框,并且它会正确地暂停,直到下一条消息(其余代码保持原样)。

我猜这是我的浏览器(Chrome)在异步线程退出之前,通过不允许DOM操作来保护免受竞争条件的魔法?或者我离开这个标记并在这里咆哮错误的树?

摆脱循环并将async设置为true可以正确接收第一条消息(其中没有问题),但显然此后没有消息。

很显然,我可以做这样的事情:

function GetMessage() 
{ 
    $.ajax({ 
     async: true, 
     url: './getPendingMessage', 
     dataType: 'json', 
     type: 'POST', 
     success: function(j) 
     { 
      $("#progressBox").append("<li>" + j.message + "</li>"); 
      if (!j.complete) 
      { 
       GetMessage(); 
      } 
     } 
    }); 
} 

但是,这将导致堆栈溢出随着时间的推移(不是吗?)。

一个显而易见的解决方案就是在这里也使用异步调用,但是要发出一个while循环来暂停并通过某种同步原语继续进行新的调用,但是似乎JS没有信号原语?

回答

0

想出了这一个 - 我不知道为什么我没有看到这个,但我的后代码片段完美的作品。我在发布的时候并没有意识到它,但是它不能溢出堆栈,因为它每次运行时都会启动异步调用并退出 - 所以堆栈帧永远不会超过2或3个深度。异步调用是外部管理的,不会在堆栈中,所以每次调用都会重新开始。

我还是很欣赏为什么第一种方法(异步调用中的同步代码)没有/不会工作的任何输入。

+0

第一种方法“无效”的原因仅仅是浏览器重画算法的一个功能。单线程同步js是阻塞的,所以只要线程正在处理浏览器就不会重绘。但是,当弹出一个警告框时,它会重新绘制(代码仍然是同步的,并且在某种意义上阻塞,但在底层,我认为就重绘算法而言它有一个等待/非阻塞状态),这就是为什么大概看起来没问题。只需实施细微差别。 – davin 2012-04-12 00:41:44

+0

但同步js代码正从一个已经异步的js线程中调用..? – 2012-04-12 05:20:03

+1

这并不重要,我不知道你为什么这么想。 “异步线程”并没有太多意义。一旦一段代码重新进入事件循环,它与其他任何代码都无法区分,因此无论它的执行是否同步开始,它的当前执行都是同步的。这就像人们来参加派对,即使有些人走路,有些人开车(同步与异步,当然不是最好的比喻),一旦他们到达,他们会逐一检查安全。 – davin 2012-04-12 09:04:36