如果我以一种“奇怪”的方式做事,我想以道歉形式作为序言,因为我主要是一名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