IE,XDomainRequest并不总是有效。

11

我正在尝试在IE上进行跨域操作。

我使用了XDomainRequest,并为所有事件(onerror、onload、onprogress和ontimeout)添加了日志记录以监控进度。

它有时候能正常工作,但不总是这样(在一个计算机上,IE9,同一站点,同一请求,3或4个中只有1个能正常工作;在另一台计算机上,IE8,也许两个请求中有1个能正常工作)。我从日志记录中没有得到任何有用的信息,因为没有触发任何事件。

我很困惑。IE有什么调试工具吗?为什么有时候XDomainRequest就不起作用呢?

非常感谢。 coronin


使用IE内置的JavaScript调试器(按F12打开)或使用免费的Visual Web Developer进行调试。 - gilly3
嗨gilly3,我尝试了F12,但没有显示任何内容。我现在正在检查Visual Web Developer~~ - coronin protein
2个回答

20

至少有两个重要的错误存在于XDomainRequest对象中,一个影响IE8,另一个影响IE9。

问题1 - 垃圾回收

在Internet Explorer 8中,XDomainRequest对象在调用send()但尚未完成时错误地受到垃圾回收。此错误的症状是开发人员工具的网络跟踪显示请求已“中止”,并且没有任何错误、超时或成功事件处理程序被调用。

典型的AJAX代码看起来像这样:

function sendCrossDomainAjax(url, successCallback, errorCallback) {
  var xdr = new XDomainRequest();
  xdr.open("get", url);
  xdr.onload = function() { successCallback(); }
  xdr.onerror = function() { errorCallback(); }
  xdr.send();
}

在这个例子中,包含XDomainRequest的变量超出了作用域。如果用户不幸,在send()异步完成之前,IE的Javascript垃圾收集器将运行并且请求将被中止。即使可以将XDomainRequest对象捕获到OnLoad和OnError事件处理程序中,IE仍然会看到整个对象图没有对它的引用,并将其垃圾回收。IE应该"固定"对象直到完成。
你会注意到互联网上有很多其他讨论提到,在xdr.send();调用周围放置setTimeout会以某种方式"解决"神秘的XDomainRequest故障。这是一种补救措施,完全不正确。发生的所有事情都是XDomainRequest对象被"固定"到setTimeout闭包中,并且不会像往常那样快速进行垃圾回收。这并不能解决问题。
为了正确地解决这个问题,请确保XDomainRequest存储在全局变量中,直到请求完成。例如:
var pendingXDR = [];

function removeXDR(xdr) {
  // indexOf isn't always supported, you can also use jQuery.inArray()
  var index = pendingXDR.indexOf(xdr);
  if (index >= 0) {
    pendingXDR.splice(index, 1);
  }
}

function sendCrossDomainAjax(url, successCallback, errorCallback) {
  var xdr = new XDomainRequest();
  xdr.open("get", url);

  xdr.onload = function() {
    removeXDR(xdr);
    successCallback();
  }

  xdr.onerror = function() {
    removeXDR(xdr);
    errorCallback();
  }

  xdr.send();
  pendingXDR.push(xdr);
}

问题2 - 缺少OnProgress事件处理程序

这个第二个问题已经被发现。Internet Explorer 9引入了一个XDomainRequest对象的回归,其中缺少(null)OnProgress事件处理程序会导致请求在尝试报告进度信息时中止。

对于快速请求,IE9从不尝试调用OnProgress事件处理程序,因此请求成功。某些条件,例如当IE由于太多打开的连接、网络延迟、服务器响应缓慢或请求或响应负载过大而延迟请求时,将导致IE9开始报告进度信息。

IE9尝试在未先检查其是否存在的情况下调用事件处理程序,XDomainRequest对象会崩溃并在内部销毁自身。

为解决此问题,请始终确保将事件处理程序附加到OnProgress上。考虑到这个错误,向所有对象的事件防御性地添加事件处理程序也不是一个坏主意。

var xdr = new XDomainRequest();
xdr.open("get", url);
xdr.onprogress = function() { };
// regsister other event handlers

其他问题

我看到有报告称,如果在调用 .open() 之前注册事件处理程序,XDomainRequest 可能会失败。为了保险起见,最好在 .open() 和 .send() 之间注册它们。我个人没有验证过这是否是一个实际的错误。

如果遇到“访问被拒绝”的错误,则是因为 XDomainRequest 不允许目标页面和主机页面之间存在不匹配的 URI 方案。换句话说,请不要从 HTTPS 页面调用 HTTP 资源。

请注意互联网上大多数 XDomainRequest 库。我查看了大部分流行的库,例如各种 jQuery AJAX 传输插件(包括此处链接的插件)。

当然,XDomainRequest 受到其所有限制和约束的影响。这些并非真正的错误,并且与替代方案(iframe 欺骗、Flash crossdomain.xml 传输)相比,它们并不是那么糟糕。

我已经在这里发布了一个新的jQuery AJAX XDomainRequest传输,使用公共领域许可证:https://github.com/ebickle/snippets/tree/master/javascript/xdomainrequest


这是一个非常棒的答案,却奇怪地被忽视了。 - Serhat Ozgel
你无法想象你帮了我多大的忙。非常感谢!我在试图修复这个错误时变得疯狂。 - GeraldIstar
这是迄今为止最好的答案。 - gabrielhpugliese
虽然有点晚了,但是在onload/onerror中放置var dummy = xdr是否可以解决xdr在完成之前被GC的问题?这将避免使用全局变量。 - Robin

11

我有完全相同的问题。简短的解决方案:

  1. 使用以下代码:https://github.com/jaubourg/ajaxHooks/blob/master/src/ajax/xdr.js

更新:链接已损坏,请在此处查找jaubourgs修复程序:https://github.com/jaubourg/ajaxHooks/blob/master/src/xdr.js

  1. 在xdr.js文件中添加xdr.onprogress = function() {};

详细信息可以在jQuery主题讨论中找到

http://bugs.jquery.com/ticket/8283

其中最后一个回复包括了xdr.onprogress fix,它起源于这个bug讨论,该讨论恰当地命名为

"IE9 RTM - XDomainRequest issued requests may abort if all event handlers not specified" http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/30ef3add-767c-4436-b8a9-f1ca19b4812e


1
非常感谢。这个答案可能为我节省了数天的工作时间。Internet Explorer 真是让我抓狂。 - Chad Decker

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