2012-03-28 195 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