1 (изменено: mikser, 2011-12-09 01:26:34)

Тема: HTA, JS: Проблема SetTimeout

Проблема точно существует в IE8, на IE 9 не тестировал.
Суть в том нельзя одновременно запустить 10 000 сеттаймаутов или больше, на 9850 ом setTimeout'e  генерируется исключение.
Во всех остальных браузерах этого ограничения нет. Только в IE.
Пытался лечить это с помощью заимствований setTimeout у new ActiveXObject("HTMLFile") но ничего не получилось,
вот пример:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
 <HEAD>
  <TITLE> New Document </TITLE>
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">.
<style>
	pre{margin:0;border-bottom:1px solid black;padding:0;}

</style>
 </HEAD>

 <BODY>
<div> A <span id=a>0</span></div>
<button onclick="go('a')">Инкрементить A 10000 раз, а потом декрементить</button>

<SCRIPT LANGUAGE="JavaScript">
function $$(a){return document.getElementById(a)}
function w(t){
		var L = document.body;
		//alert([t,L.insertAdjacentHTML])
		if (L.innerHTML.length>50000)
		{
			L.innerHTML="";
		}
        L.insertAdjacentHTML("beforeEnd", "<pre>" + t.replace(/</g,"&lt;") + "\n<\/pre>")

        L.scrollTop = L.scrollHeight; //1 << 20;
	};
///////////////////////////
$setTimeout.counts=[0];
$setTimeout.okna=[new ActiveXObject("HTMLFile")];
$setTimeout.okna[0].open()
function $setTimeout(callback,delay)
{	
	var i=$setTimeout.counts.length-1
	var c=$setTimeout.counts[i]
	if (c>3000)
	{
		$setTimeout.counts.push(0);
		$setTimeout.okna.push(new ActiveXObject("HTMLFile"));
		i=$setTimeout.counts.length-1;
		$setTimeout.okna[i].open();
		w("$setTimeout.counts = " + $setTimeout.counts)
		w("$setTimeout.okna = " + $setTimeout.okna)
	}

	++$setTimeout.counts[i];
	var timer = $setTimeout.okna[i].parentWindow.setTimeout(callback,delay)
	return {"timer":timer, "okno":i}

}
////////////////////////////
window.onload=function()
{
/*
	var p=new ActiveXObject("HTMLFile");
	var A="Hello from HTMLfile!"
	p.open();
	p.parentWindow.setTimeout(function(){alert(A)},2000)
*/
	
}
////////////////////////////


	function foo(obj,bar)
	{
		obj.innerHTML=parseInt(obj.innerHTML)+1;
		var et;
		try
		{                 //$setTimeout
			et=setTimeout(function(){
				obj.innerHTML=parseInt(obj.innerHTML)-1;
			},7000);
			
		}

		catch (e)
		{
				w("et = "+(typeof et))
				w("Ошибка "+e.description)
		}
		
				


	}

	function go(let)
	{
		var i=0;
		var len=10000;//4925;//9850
		for (;i<len ;++i )
		{
			foo($$(let),i)
		}
	}

  </SCRIPT>
 </BODY>
</HTML>

Вопрос - как преодолеть это ограничение?

Самое гадкое в этой не доработке МС, что если убрать трай катч то даже не поймешь в чем дело, ИЕ не выдаст ни каких внятных сообщений об ошибке.

2

Re: HTA, JS: Проблема SetTimeout

mikser пишет:

как преодолеть это ограничение?

Скорее всего - никак. Возможно ограничение связано с ограничением по памяти - ведь каждый таймаут создается в отдельном контексте, который требует расходов памяти.

Поделитесь, пожалуйста, что это у вас за задача такая, что Вам требуется одновременно 10к таймаутов? Насколько я помню, дискретность таймеров в JS примерно 10 мсек, то есть таймер не будет выполняться чаще чем раз в 10 мсек. Мне кажется Вам следует пересмотреть архитектуру приложения.

( 2 * b ) || ! ( 2 * b )

3 (изменено: mikser, 2011-12-09 13:13:40)

Re: HTA, JS: Проблема SetTimeout

Я имел ввиду может есть какие нить сторонние ActiveX с таймером на которые можно повесить JS хэндлеры?
Кстати мне точность до мсек не важна. Главное что бы отложеная задача в конечном итоге была выполнена.

Возможно ограничение связано с ограничением по памяти - ведь каждый таймаут создается в отдельном контексте

Тут я с вами не согласен. Все сет таймаутные и сет интервальные функции выполняются в контексте глобального обекта window. То есть this == window (внутри функции которая вызывается по сетТаймауту). А если вы имели в виду замыкания как то в примере внутри функции foo объект obj  на который ссылается безымянная функция вызываемая по сетТаймауту, то ответ опять таки нет - дело не в этом. Тут можно отказаться от использования замыкания, использовав вместо функции, простую строку, объект HTML передвавать по его ID (HTML аттрибут ID - строка). Я так делал и увидел что ошибка IE по прежнему вызывается следовательно замыкания тут не причем.

4

Re: HTA, JS: Проблема SetTimeout

mikser:

Самое гадкое в этой не доработке МС, что если убрать трай катч то даже не поймешь в чем дело, ИЕ не выдаст ни каких внятных сообщений об ошибке.

А пункты в настройках IE, в разделе "Дополнительно" Вы выставляли ?
а) Отключить отладку сценариев (Internet Explorer)
б) Отключить отладку сценариев другие
в) Показывать уведомление о каждой ошибке сценария

2) Абсолютно соглашусь с Rumata:

Поделитесь, пожалуйста, что это у вас за задача такая, что Вам требуется одновременно 10к таймаутов?
....
Мне кажется Вам следует пересмотреть архитектуру приложения.

3) Как ещё один из вариантов - у IE есть особенность останавливать скрипты, выполнение которых занимает либо слишком много времени. Но это уже чисто предположение.

Попробую сегодня сэмулировать ситуацию.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

5 (изменено: mikser, 2011-12-09 14:04:33)

Re: HTA, JS: Проблема SetTimeout

Xameleon пишет:

А пункты в настройках IE, в разделе "Дополнительно" Вы выставляли ?
а) Отключить отладку сценариев (Internet Explorer)
б) Отключить отладку сценариев другие
в) Показывать уведомление о каждой ошибке сценария

Выставил. Всеравно сообщений об ошибке не замечено.

Xameleon пишет:

Попробую сегодня сэмулировать ситуацию.

Достаточно сохранить мой код в html файл и запустить.

6

Re: HTA, JS: Проблема SetTimeout

mikser пишет:

Достаточно сохранить мой код в html файл и запустить.

Не люблю я Javascript и длинный код.


<html>
    <head>
        <meta charset="Windows-1251">
    </head>
    <body>
        <div id="txtfld">0</div>
        <button onclick="btn1_onclick()">Вызвать Test1()</button>
        <button onclick="btn2_onclick()">Вызвать Test2()</button>
    </body>

    <script language=vbscript>
    Option Explicit
    Dim n
    Sub btn1_onclick()
        Dim i
        For i=1 to 10000
            SetTimeout GetRef("Test1"), i
        Next
    End Sub

    Sub btn2_onclick()
        Dim i
        For i=1 to 10000
            SetTimeout GetRef("Test2"), i
        Next
    End Sub
    
    Sub Test1()
        n = n + 1
        txtfld.innerText = n
    End Sub

    Sub Test2()
        n = n - 1
        txtfld.innerText = n
    End Sub
    
    </script>

</html>

Проверил на 10000 таймеров в IE. Всё пашет.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

7 (изменено: mikser, 2011-12-09 16:37:06)

Re: HTA, JS: Проблема SetTimeout

У хамелеона в IE9 все пашет. У меня не пашет в IE8. Отказываться от поддержки IE8 нет желания, так как он поставляется вместе с win7 по умолчанию. А обновлять браузер который как браузер не использую я не хочу из принципа.

8

Re: HTA, JS: Проблема SetTimeout

mikser, в очередной раз словами, уважаемого Rumata напоминаю:

Поделитесь, пожалуйста, что это у вас за задача такая, что Вам требуется одновременно 10к таймаутов?
....
Мне кажется Вам следует пересмотреть архитектуру приложения.

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

9

Re: HTA, JS: Проблема SetTimeout

Ну, до кучи, и я присоединю свой голос к высказанной просьбе.

10

Re: HTA, JS: Проблема SetTimeout

Может это чем поможет
Можно к хозяину сайта обратиться.

А вообще то осёл вешается на больших циклах, что взять с убогого.
А может осёл просто в какой то момент перестаёт успевать отслеживать все таймеры?

Мне тоже любопытно, зачем столько таймеров одновременно.

11

Re: HTA, JS: Проблема SetTimeout

mikser пишет:

Все сет таймаутные и сет интервальные функции выполняются в контексте глобального обекта window. То есть this == window (внутри функции которая вызывается по сетТаймауту).

Обратите внимание как я сказал:

Rumata пишет:

каждый таймаут создается в отдельном контексте

Я не мог вспомнить другой перевод (точнее, определение) слова scope, активно используемого в JavaScript. Вот оно - область видимости (спасибо коллеге Любитель за ссылку - я о ней знаю, но в данный момент успешно забыл). Так вот, для каждой функции создается своя область видимости. Посему Ваше следующее высказывание в некоторой степени не соответствует сказанному мной.

mikser пишет:

Все сет таймаутные и сет интервальные функции выполняются в контексте глобального обекта window

То есть существуют область видимости (контекст создания - я имел в виду это) и область исполнения (то, что имели в виду Вы). Просто сравните результаты двух таймаутов (строка - через 1 сек, функция - через 10 сек)

<script>

var x = 1;

(function()
{
    var x = 1000;
    setTimeout('alert(x)', 1000);
    setTimeout(function() { alert(x); }, 10000);

})();

</script>

Но! Если вместо alert(x) Вы укажете alert(this === window), то в обоих случаях получите результат - true. И это верно - потому что контекст вызова - глобальный. Это то, опять же, о чем говорили Вы.

( 2 * b ) || ! ( 2 * b )

12

Re: HTA, JS: Проблема SetTimeout

Rumata пишет:

Просто сравните результаты двух таймаутов (строка - через 1 сек, функция - через 10 сек)

А нельзя ли для не профессионалов на пальцах показать, что сравнивать, как сравнивать, что за результаты?

13

Re: HTA, JS: Проблема SetTimeout

Любитель, для полноты иллюстрации я объединил все сказанное в один скрипт. Каждый вызов установки таймера подробно описан в предшествующих комментариях. Вы можете сами его запустить и проверить - достаточно скопировать весь пример из этого сообщения и сохранить как html-страницу.

<script>

var x = 1;

(function()
{
	var x = 1000;

/*
Для этого таймаута контекст создания и контекст вызова совпадают. 
Поэтому через одну секунду после открытия страницы в браузере 
первое диалоговое окно покажет следующий текст
1, true
*/
	setTimeout('alert([x, this == window])', 1000);

/*
Для этого таймаута контекст создания и контекст вызова не совпадают. 
Контекст создания функции локальный - функция видит локальную переменную. 
Поэтому через десять секунд после открытия страницы в браузере 
второе диалоговое окно покажет следующий текст
1000, true
*/
	setTimeout(function() { alert([x, this == window]); }, 10000);

})();

</script>
( 2 * b ) || ! ( 2 * b )

14

Re: HTA, JS: Проблема SetTimeout

Rumata пишет:

достаточно скопировать весь пример из этого сообщения и сохранить как html-страницу.

Что я и сделал.
Спасибо, посмотрю.


А товарищЪ mikser так и не откликнулся на многочисленные просьбы "трудящихся"....

15

Re: HTA, JS: Проблема SetTimeout

Коллега mikser, небось, ботнет пишет, потому шифруется .

16 (изменено: mikser, 2011-12-11 02:51:00)

Re: HTA, JS: Проблема SetTimeout

Не ботнет, а безобидный авторегистратор мейл боксов на mail.ru  (не для того о чем вы могли подумать, если интересно зачем пишите в аську)
Написал "автомат" который делает последовательно несколько асинхроных http запросов в результате чего регистрируется почтовый ящик. Запросы асинхроные и последовательные потому не обойтись без сет таймаутов или интервалов. Если запустить одновременно 50 автоматов то через какое то время в самый разгар работы скрипт просто останавливается без сообщений об ошибках.
Из за чего может останавливаться HTA на IE8 без сообщений об ошибках? Вот нашел сеттаймауты с ними проблема такая имеет место быть но 10000 многовато действительно, столько в моем скрипте врятли могло создаться одновременно, что еще может быть?

Rumata пишет:

scope

Да, это было второе о чем я подумал и написал  - замыкания.
Области видимости создаются и без сеттаймаутов. Можете создать 10к scop'ов и убедится что дело не в них. Или вам не удалось повторить баг. У вас IE9?
Кстати коллеги,  надеюсь вы не просто пофлудить сюда зашли , пожалуйста отпишитесь, удалось ли вам повторить этот баг IE8?


Rumata пишет:
mikser пишет:

как преодолеть это ограничение?

Скорее всего - никак.

Оказалось что если нельзя но очень хочется то можно .
В случае с Сеттаймаутом можно все повесить на один сетинтервал. Да конечно со всеми вытекающими: нагрузка на процессор, больше кушается памяти и потеря точности, но ведь работает !

Рабочий пример (ctrl+C, ctrl+V, сохранить в html, запустить в IE8 или ниже):


<BODY>
<div> A <span id=a>0</span></div>
<div> B <span id=b>0</span></div>
<button onclick="go('a')">Инкрементить A а потом декрементить</button>
<button onclick="go('b')">Инкрементить B а потом декрементить</button>
<button onclick="clearAll(tm)">Отменить все активные декременты</button>

<SCRIPT LANGUAGE="JavaScript">
function $$(a){return document.getElementById(a)}

function $setTimeout(callback,delay)
{
	if (!$setTimeout.Init)
	{
		$setTimeout.Init=true;
		$setTimeout.INT=setInterval($TimersFire,11);
		$setTimeout.TIMERS={};
		$setTimeout.IDCOUNT=1;

	}

	var ID=$setTimeout.IDCOUNT++;
	$setTimeout.TIMERS[ID]={time:(new Date()).valueOf()+delay, func:callback}
	return ID;
}
////////////////////////////

function $clearTimeout(ID)
{
	if ($setTimeout.TIMERS[ID])
		delete $setTimeout.TIMERS[ID];
}

////////////////////////////
function $TimersFire()//вызывается раз в 11 мсек
{
	var nov=(new Date()).valueOf();
	var TS=$setTimeout.TIMERS;
	for(var i in TS)
	{
		if (TS[i].time <= nov)
		{
			TS[i].func();
			delete TS[i]
		}
		else //оптимизация :) 
			break;
	}//for


	//============================================

}
////////////////////////////
function clearAll(mas)
{
	while (mas.length>0)
	{
		$clearTimeout(mas.pop())
	}
}
	
	var tm=[];
	function foo(obj,bar)
	{
		obj.innerHTML=parseInt(obj.innerHTML)+1;
		var ID=obj.getAttribute("id");
		var handler=new Function("","var SPAN=$$('"+ID+"');SPAN.innerHTML=parseInt(SPAN.innerHTML)-1;")
		var et=$setTimeout(handler,3000);
		tm.push(et)
	}

	function go(let)
	{
		var i=0;
		var len=10000;//4925;//9850
		for (;i<len ;++i )
		{
			foo($$(let),i)
		}
		//w("now = " + Dumper(new Date()))
		//w(Dumper(timers))
	}

  </SCRIPT>
 </BODY>

Осталось только придумать  $setInterval.

17

Re: HTA, JS: Проблема SetTimeout

mikser пишет:

удалось ли вам повторить этот баг

Ваш скрипт я запускал, но не понял его принципа. Я наблюдал декремент от 10000 до числа чуть меньше 20. Верно ли, что я наблюдал создание порядка 9980 таймеров? мсие 7 на висте.

( 2 * b ) || ! ( 2 * b )

18

Re: HTA, JS: Проблема SetTimeout

mikser пишет:

Не ботнет, а безобидный авторегистратор мейл боксов на mail.ru  (не для того о чем вы могли подумать, если интересно зачем пишите в аську)

А зачем Вам столько почтовых ящиков? Ну, не приходят на ум иные целевые назначения.

mikser пишет:

Кстати коллеги,  надеюсь вы не просто пофлудить сюда зашли smile , пожалуйста отпишитесь, удалось ли вам повторить этот баг IE8?

Ну, Вы сами этому поспособствовали, дав пищу для измышлений, не отвечая на вопрос о назначении. А IE8 у меня уже нет.

19 (изменено: mikser, 2011-12-11 17:38:33)

Re: HTA, JS: Проблема SetTimeout

Rumata пишет:

Ваш скрипт я запускал, но не понял его принципа. Я наблюдал декремент от 10000 до числа чуть меньше 20. Верно ли, что я наблюдал создание порядка 9980 таймеров? мсие 7 на висте.

Скрипт увеличивает число на 1, а потом создает таймер чтобы через несколько секунд уменьшить таймер на 1, и так 10к раз. Судя по всему у вас баг повторился, так как счетчик не был обнулён.