Проблема проявляется в IE6, в FireFox 3.0.10 не воспроизводится.
Сут проблемы в следующем. На страничке есть динамический контент, который подгружается асинхронно средствами XMLHttpRequest. Эта страничка открывается в новом окне по ссылке с другой странички, собственно, если закрыть это самое новое окно пока XMLHttRequest не вернет данные и повторить это несколько раз то после определнного момента, страница перестает открываться в этом самом новом окне.
Собственно нужно понять почему, и заставить страницу работать и не повисать в IE6 в указанном выше сценарии. (т.е. что бы можно было открывать ее сколько угодно раз кликая по ссылке и закрывать новое окно не заботясь загрузились ли динамически данные до конца или нет)
Теперь более подробнее то что мне удалось выяснить, здесь и прошу вашей помощи, может кто-то сталкивался или имеет достаточно знаний в JavaScript под IE что бы посоветовать куда копать дальше.
Мои тесты показали, что для того, что бы страница начала зависать при открытии в новом окне достаточно закрытием окна броузера оборвать динамическую загрузку данных 2 раза. После этого функционал динамической загрузки данных (т.е. все что связано с XMLHttpRequest) перестает работать и при открытии страничек в новом окне по упомянутой ссылки и собственно на исходной родительской странице тоже все связанное с XMLHttpRequest перестает работать.
После этого предположил, что экземпляр XMLHttpRequest, а в IE это на самом деле объект DOM, остается повисшим в памяти, что и приводит к указанной проблеме. Переделал код на использования подхода с проверкой свойства readyState по таймеру вместо установки свойства onreadystatechanged (подход обсуждался здесь на форуме, также упомянут в источнике
http://xmlhttprequest.ru/#problem).
Проверил страницу на наличие утечек памяти с помощью JSLeaksDetector - утечек не обнаружил. (ссылки по теме утечек памяти в IE:
http://blogs.msdn.com/gpde/pages/jav...-detector.aspx,
http://www.codeproject.com/KB/script...kpatterns.aspx)
Собственно здесь я пришел в тупик. Заранее спасибо за любые мнения и советы!
Ниже код класса, который я написал для выполнения асинхронных запросов, он по идее должен поддерживать выполнения нескольких одновременных асинхронных запросов. (в IE, если не ошибаюсь, существует ограничение в 2 запроса, ну у меня собственно 2 асинхронных запроса и используются). И соответсвенно из кроссброузерности волнует только IE и FireFox, поэтому класс может быть получился и не универсальным. Замечания к классу тоже привествуются.:
!!! Внимание, ниже представлен откорректированный код, который содержит исправление описанной проблемы, исправления выделены красным, подробности см. в следующем посте.
Код:
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Asynchronous caller class (uses AJAX technique).
// It avoids cases that lead memory leaks in IE6 as it doesn't set onreadystatechange property.
//////////////////////////////////////////////////////////////////////////////////////////////////////
// Constructor.
// completeCallback - callback that will be executed when request is completed
// state - (optional parameter) state parameter that will be supplied as 4-th argument of completeCallback function when it is called
function AsyncCaller(completeCallback, state)
{
if (!completeCallback)
throw "Callback is not specified for AsyncCaller constructor";
var httpReq = this.GetHttpRequest();
if (!httpReq)
throw "Unable to create an instance of XMLHttpRequest.";
this.httpReq = httpReq;
this.completeCallback = completeCallback;
this.state = state;
this.pollInterval = 10; // ms.
// Ensure async call is aborted when document is unloaded
var self = this;
this.OnWindowUnload = function() { self.AbortExecution(); }
AddEvent(window, "unload", this.OnWindowUnload);
}
// Returns a new initialized instance of XMLHttpRequest class
AsyncCaller.prototype.GetHttpRequest = function()
{
var result = null;
if (window.XMLHttpRequest)
{
// Mozilla, Safari,
result = new XMLHttpRequest();
if (result.overrideMimeType)
{
result.overrideMimeType('text/xml');
}
}
else if (window.ActiveXObject)
{
// IE
try
{
result = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
try
{
result = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e)
{}
}
}
return result;
}
// Call GET or POST method asynchronously. When it is completed callback passed to constructor will be executed
AsyncCaller.prototype.Call = function(uri, postRequest, headers)
{
var method, body;
if (postRequest)
{
method = "POST";
body = postRequest;
}
else
{
method = "GET";
body = null;
}
this.httpReq.open(method, uri, true);
this.httpReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
this.httpReq.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
this.httpReq.setRequestHeader("Cache-Control", "no-cache");
if (headers)
{
for (var i = 0; i < headers.length; i++)
{
if (headers[i] && headers[i] != "")
{
var colonIndex = headers[i].indexOf(":");
if (colonIndex != -1)
{
var headerName = headers[i].substring(0, colonIndex);
var headerValue = headers[i].substring(colonIndex + 1);
this.httpReq.setRequestHeader(headerName, headerValue);
}
}
}
}
this.StartTimer();
this.httpReq.send(body);
}
// Internal callback that is called with the interval defined by property 'pollInterval'
// untill request is completed
AsyncCaller.prototype.OnReadyStateChange = function()
{
if (this.httpReq.readyState == 4)
{
this.StopTimer();
RemoveEvent(window, "unload", this.OnWindowUnload);
this.completeCallback(this.httpReq.status, this.httpReq.statusText, this.httpReq.responseText, this.state);
}
else
{
var self = this;
this.timerID = setTimeout(
function()
{
self.OnReadyStateChange();
},
this.pollInterval);
}
}
// Start timer (pooling of the state of ajax response)
AsyncCaller.prototype.StartTimer = function()
{
this.StopTimer();
var self = this;
this.timerID = setTimeout(
function()
{
self.OnReadyStateChange();
},
this.pollInterval);
}
// Stop timer
AsyncCaller.prototype.StopTimer = function()
{
if (this.timerID)
{
clearTimeout(this.timerID);
this.timerID = null;
}
}
// Aborts execution of async request
AsyncCaller.prototype.AbortExecution = function()
{
this.StopTimer();
this.httpReq.abort();
}
// Register event for the element
function AddEvent(elem, type, handler){
if (elem.addEventListener){
// DOM compliant browsers (Mozilla, FF, ...)
elem.addEventListener(type, handler, false)
}
else
{
// IE
elem.attachEvent("on"+type, handler)
}
}
// Remove event for the element
function RemoveEvent(elem, type, handler){
if (elem.removeEventListener){
// DOM compliant browsers (Mozilla, FF, ...)
elem.removeEventListener(type, handler, false)
}
else
{
// IE
elem.detachEvent("on"+type, handler)
}
}
|