IE, XDomainRequest not always work

*爱你&永不变心* 提交于 2019-11-27 13:44:25

There are at least two significant bugs in the XDomainRequest object, one that affects IE8 and another that affects IE9.

Issue 1 - Garbage Collection

In Internet Explorer 8, the XDomainRequest object is incorrectly subject to garbage collection after send() has been called but not yet completed. The symptoms of this bug are the Developer Tools' network trace showing "Aborted" for the requests and none of the error, timeout, or success event handlers being called.

Typical AJAX code looks a bit like this:

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

In this example, the variable containing XDomainRequest goes out of scope. If the user is unlucky, IE's Javascript garbage collector will run before send() asynchronously completes and the request will be aborted. Even though the XDomainRequest object can be captured into the OnLoad and OnError event handlers, IE will see that that entire object graph has no references to it and will garbage collect it. IE should be "pinning" the object until complete.

You'll notice quite a few other discussions on the internet mentioning that placing a setTimeout around the xdr.send(); call will somehow "solve" mysterious XDomainRequest failures. This is a kludge, and completely incorrect. All that's happening is that the XDomainRequest object is being "pinned" into the setTimeout closure and not subject to garbage collection as quickly. It doesn't solve the problem.

To correctly work around this issue, ensure the XDomainRequest is stored in a global variable until the request completes. For example:

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);
}

Issue 2 - Missing OnProgress EventHandler

This second issue is already known. Internet Explorer 9 introduced a regression in the XDomainRequest object where a missing (null) OnProgress event handler would cause the request to abort when it tries to report progress information.

For fast requests, IE9 never attempts to call the OnProgress event handler and the request succeeds. Certain conditions, such as when IE delays the request due to too many open connections, network latency, slow server responses, or large request or response payloads will cause IE9 to start to report progress information.

IE9 tries to call the event handler without first checking it exists, and the XDomainRequest object crashes and destroys itself internally.

To solve this issue, always ensure an event handler is attached to OnProgress. Given the bug, it's not a bad idea to defensively add event handlers to all of the object's events.

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

Other Issues

I've seem reports that XDomainRequest can fail if the event handlers are registered before .open() is called. Again, defensively, it's not a bad idea to register them between the .open() and .send() calls. I haven't personally verified whether it's an actual bug.

If you run into an "Access Denied" error, it's because XDomainRequest doesn't allow mismatched URI schemes between the target and host page. In other words, try don't call an HTTP resource from an HTTPS page.

Beware most of the XDomainRequest libraries on the internet. I looked at most of the popular ones, such as the various jQuery AJAX transport plugins (including the ones linked in another answer here).

And, of course, XDomainRequest is subject to all of it's normal limitations and constraints. These aren't bugs per-se, and compared with the alernatives (iframe kludges, Flash crossdomain.xml transports) they're not that bad.

I've posted a new jQuery AJAX XDomainRequest transport under a public domain license here: https://github.com/ebickle/snippets/tree/master/javascript/xdomainrequest

Martin Kool

Had the exact same question. Short solution:

  1. Use this code: https://github.com/jaubourg/ajaxHooks/blob/master/src/ajax/xdr.js

UPDATE: link is broken, find jaubourgs fix here: https://github.com/jaubourg/ajaxHooks/blob/master/src/xdr.js

  1. Add xdr.onprogress = function() {}; in that xdr.js file

Details can be found on the jQuery topic discussion here

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

in which the last reply included that xdr.onprogress fix which originated from this bug discussion which was aptly titled

"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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!