如何在Javascript中创建、合并和销毁XMLHttpRequest对象

9

我的背景是传统的编译型面向对象语言,如C++和.NET编程,现在我正在涉猎一些JavaScript用于一个新项目。我正在尝试使用AJAX,并遇到了一个关于浏览器如何管理对象的令人困惑的方面。

[编辑#2] - 活动内容脚本更改

我有一个练习页面,其中包含三个按钮,每个按钮都使用XMLHttpRequest对象更新<textarea>

  1. 按钮1使用slowtime.php的文本内容更新TextArea1
  2. 按钮2使用slowtime.php的文本内容更新TextArea2
  3. 按钮3使用fasttime.php的文本内容更新TextArea3

其中slowtime.phpfasttime.php是简单的脚本,返回一个带有两个时间戳的文本/HTML页面:一个是页面加载时的时间戳,另一个是经过一段时间后的时间戳。

当一个按钮被单独点击时,每个按钮都可以正常工作。如果我在第一个请求完成之前点击按钮2和按钮3,则更新仍然按预期工作。

如果我在第一个请求完成之前点击按钮1和按钮2,则TextArea1和TextArea2将接收到正确的值;然而,onreadystatechange事件调用同时发生,即第一个响应很晚,只有在第二个响应到达时才会被处理。

示例代码

网站

<!DOCTYPE html>
<html>
<head>
<script>
function loadXMLDoc(url,target)
{
var xmlhttp;
xmlhttp=new XMLHttpRequest();

xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    document.getElementById(target).value=xmlhttp.responseText;
    }
  }
xmlhttp.open("POST",url,true);
xmlhttp.send();
}
</script>
</head>

<body>
<form>

<input type="button" value="Button 1" onClick="loadXMLDoc('slowtime.php','TextArea1')"/>
<input type="button" value="Button 2" onClick="loadXMLDoc('slowtime.php','TextArea2')"/>
<input type="button" value="Button 3" onClick="loadXMLDoc('fasttime.php','TextArea3')"/>

<div><textarea id="TextArea1"></textarea></div>
<div><textarea id="TextArea2"></textarea></div>
<div><textarea id="TextArea3"></textarea></div>

</form>
</body>
</html>

PHP 代码(slowtime.php

<?php
     echo date('h:i:s') . "\n";
     sleep(5);
     echo date('h:i:s') . "\n";
?>

问题 [修订版]

浏览器如何管理XMLHttpRequest对象?按下2和3按钮表示每次按下都会实例化一个新对象,每个对象都有独立的事件处理程序。如果这些对象在初始函数调用之后仍然存在(因为它们的事件处理程序仍然存在),那么它们何时会从内存中清除/销毁?

如果XMLHttpRequests是单独的对象,那么向同一URL发送第二个请求会如何影响第一个请求的响应时间?这可能是一个服务器端的问题吗?


1
@JoshuaD.Boyd 这不应该做任何其他事情,因为 xmlhttp 变量在 loadXMLDoc 内部是局部作用域。 - Tyilo
@Ragnagord,我已经使用不同的浏览器测试了该网站,并在问题中包含了观察结果。 - nicholas
1
@nicholas,能否包含 slowrand.phpfastrand.php 的源代码? - seliopou
感觉像是缓存问题。你可以在 xmlhttp.send() 之前加上 xmlhttp.setRequestHeader("If-Modified-Since", new Date(0)); 吗?另外,正如上面所述,任何浏览器都不应该“合并”请求。 - bkconrad
1
@seliopou:你带领我找到了服务器端代码的一个重大问题。为了诊断,slowrand正在打开和写入文件。第二个slowrand调用试图打开一个被锁定的文件,因此在错误退出 - 长话短说,使用不同的PHP代码来写时间戳,我得到了不同的结果。请求没有合并,但它们的时间被搞乱了。 - nicholas
显示剩余6条评论
2个回答

2

如果没有在对象上显式调用delete,XMLHttpRequest上下文将永远不会被删除。在这种情况下:xmlhttp。如果您希望应用程序运行得更加流畅和干净,确实应该以某种方式跟踪该变量并清理它。Javascript最初是为网页设计的,因此它往往会让事情变得混乱,除非您自己防止。

否则,一旦对象不再可由任何其他函数使用或具有任何剩余回调,浏览器的垃圾收集器将消除该对象。

至于同时发生事件的问题,我自己无法重现该问题,这使我相信您的php配置存在问题。您的服务器是否允许同时运行多个脚本?

以下是在我的服务器上对您的示例进行了一些微小更改后的结果:http://www.seijinohki.net/test.php


感谢您在不同的测试服务器上发布代码。当我在Chrome或Safari中测试您的网站时,我得到与上面的Rambo相同的结果 - 两个文本区域获得相同的值。同时,Firefox,IE9和Silk则没有这种情况。您的页面在Maxthon移动浏览器中根本无法工作。 - nicholas
经过很长时间的考虑,我接受这个答案,因为它直接解决了请求的上下文。这是一个有趣的讨论,并且表明,至少目前来看,Web开发/浏览器等在执行脚本方面仍然存在不一致性。 - nicholas

1

这是一个浏览器问题。

  • Chrome 23&26会出现这种情况。
  • Opera 12不会。
  • IE 9不会。
  • Firefox 17不会。

如果您使URL唯一,例如slowtime.php?1slowtime.php?2,那么Chrome就不会再出现这种情况了。

顺便说一句,在我的测试中,Chrome使用相同的值更新两个文本区域 - 来自第一个请求的值。并且它们都在第一个请求完成时更新,而不是在第二个请求完成时更新。这肯定是一个错误,因为它是完全错误的。通过Web服务器日志验证,第二个请求从未被发送。


当我在kokorohakai的服务器上使用Chrome或Safari进行测试时,我看到与您相同的结果-两个文本区域都获得相同的值,并且与第一个请求一起计时。同时,在我的测试服务器(localhost)上,我获得了两个不同的值,与第二个请求一起计时。这使我相信答案确实非常复杂,并且基于浏览器和服务器的行为。 - nicholas
@nicholas,你的本地服务器日志是否显示了两个请求?然而,在我的测试中Chrome只发送了一个请求这一事实表明它存在一个错误(或者是一个奇怪的特性)。 - goat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接