如果我以一種「奇怪」的方式做事,我想以道歉形式作爲序言,因爲我主要是一名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沒有信號原語?
第一種方法「無效」的原因僅僅是瀏覽器重畫算法的一個功能。單線程同步js是阻塞的,所以只要線程正在處理瀏覽器就不會重繪。但是,當彈出一個警告框時,它會重新繪製(代碼仍然是同步的,並且在某種意義上阻塞,但在底層,我認爲就重繪算法而言它有一個等待/非阻塞狀態),這就是爲什麼大概看起來沒問題。只需實施細微差別。 – davin 2012-04-12 00:41:44
但同步js代碼正從一個已經異步的js線程中調用..? – 2012-04-12 05:20:03
這並不重要,我不知道你爲什麼這麼想。 「異步線程」並沒有太多意義。一旦一段代碼重新進入事件循環,它與其他任何代碼都無法區分,因此無論它的執行是否同步開始,它的當前執行都是同步的。這就像人們來參加派對,即使有些人走路,有些人開車(同步與異步,當然不是最好的比喻),一旦他們到達,他們會逐一檢查安全。 – davin 2012-04-12 09:04:36