Javascript-форум (https://javascript.ru/forum/)
-   Ваши сайты и скрипты (https://javascript.ru/forum/project/)
-   -   FTread, исполнение функций в отдельных потоках (https://javascript.ru/forum/project/7558-ftread-ispolnenie-funkcijj-v-otdelnykh-potokakh.html)

tenshi 08.02.2010 00:41

FThread, исполнение функций в отдельных потоках
 
Фабрика, создающая обёртку, которая запускает функцию в отдельном потоке в результате чего её падение не приводит к падению запустившего её кода. потоки не параллельны!

var FThread= new function(){
Version: 3
Descr: 'creates a wrapper for function that allows you to not be afraid of exceptions in'
License: 'public domain'

Implementation:
var root= document.documentElement
var FThread= function( proc ){
	var thread= root.addEventListener
	? function( ){
		var res, self= this, args= arguments
		root.addEventListener( 'launch thread', function( ){
			root.removeEventListener( 'launch thread', arguments.callee, false )
			res= thread.proc.apply( self, args )
		}, false )
		var event= document.createEvent( 'UIEvents' )
        event.initEvent( 'launch thread', false, false )
        root.dispatchEvent( event )
		return res
	}
	: function( ){
		var res, self= this, args= arguments
		root.attachEvent( 'onpropertychange', function( ){
			root.detachEvent( 'onpropertychange', arguments.callee )
			delete root[ 'launch thread' ]
			res= thread.proc.apply( self, args )
		} )
		root[ 'launch thread' ]= null
		return res
	}
	thread.proc= proc
	return thread
}

Export: return FThread

Usage:
var inverse= FThread(function( a ){
	if( a === 0 ) throw 'division by zero'
	return 1/a
})
console.log([ inverse( 0 ), inverse( 1 ) ])
// prints [ undefined, 1 ] and exception 'division by zero' in console log 

}
код не актуален ввиду новой версии: http://javascript.ru/forum/project/7...html#post44189

Мараторий 08.02.2010 01:52

Интересно, правда не очень представляю прикладного применения.

Зато я узнал, что можно вот так вот сделать:
function A(flag) {
  this.a = 1;
  if(flag == 1) return {c: 3}; // typeof == "object"
  if(flag == 2) return 4; // typeof != "object"
}
A.prototype.b = 2;
console.log(new A()); // {a:1, b: 2}
console.log(new A(1)); // {c: 3}
console.log(new A(2)); // {a:1, b: 2}

tenshi 08.02.2010 02:04

применение - независимая инициализация модулей, независимое исполнение обработчиков событий и тп

Gvozd 08.02.2010 02:27

Цитата:

Сообщение от tenshi
потоки не параллельны!

заметка: и даже не ассинхронны
почерпнул для себя кое-что новое
вот только, исключение нигде не выползает в консоль(Opera, Mozilla)

Разве что, не вижу смысла зачем городить потоки для данной задачи.
Сделал вот такое решение. Кто-то видит подводные камни какие?
var GThread= new function(){
//Version: 3
//Descr: 'creates a wrapper for function that allows you to not be afraid of exceptions in'
//License: 'public domain'

//Implementation:
var GThread=function(proc){
	var thread=function()
		{
		try
			{
			return proc.apply(this,arguments);
			}
		catch(e)
			{
			console.warn(e); 
			console.trace();
			return undefined;
			}
		}
	return thread;
}

//Export: return GThread

//Usage:
var inverse=GThread(function(a){
	if(a===0) 
		throw 'division by zero';
	return 1/a;
})
var inverse2=GThread(function(a){
	return -a;
})
console.log([inverse(0),inverse2(2),inverse(2)])
// prints [undefined, -2, 0.5] without( :( ) exception 'division by zero' in console log 

}

tenshi 08.02.2010 02:32

Цитата:

вот только, исключение нигде не выползает в консоль(Opera, Mozilla)
не может этого быть. специально чтобы исключения обрабатывались браузером и делалось.

tenshi 08.02.2010 02:36

> Сделал вот такое решение. Кто-то видит подводные камни какие?
жопа с отладкой затрайкатченного кода

Gvozd 08.02.2010 02:51

Цитата:

Сообщение от tenshi
не может этого быть. специально чтобы исключения обрабатывались браузером и делалось.

разве?
Opera 10.10 с активной консолью ошибок - молчок
Firefox 3.5.7 с firebug-ом - молчок
IE 8.0.6001 - молчок
Более того результаты alert-a:
Цитата:

Сообщение от Opera
<localhost>

,

Цитата:

Сообщение от IE
---------------------------
Сообщение с веб-страницы
---------------------------
,
---------------------------
ОК
---------------------------

и только огнелис радует:
Цитата:

,1
мой скрипт везде выводит одинаково
Цитата:

,-2,0.5
Цитата:

Сообщение от tenshi
жопа с отладкой затрайкатченного кода

в смысле что исключения нигде не видны?
у тебя также ни единого исключения не было проброшено
кстати, ничто не мешает бросить console.error(e);
поправил свой код по этому поводу

PeaceCoder 08.02.2010 02:54

А кто мешает сделать так ?
catch(e)
  {
  if (console) console.error(e);
  return undefined;
  }

Gvozd 08.02.2010 02:57

PeaceCoder,
пока писал и правил свое сообщение, вы уже написали один из моих моментов)))

tenshi 08.02.2010 03:09

гм.. что-то не то.. ща поковыряюсь..

tenshi 08.02.2010 03:11

Цитата:

А кто мешает сделать так ?
найди потом имя файла и номер строки...

Gvozd 08.02.2010 03:25

фак
при вставке последовательно скрипта tenshi, а затем моего, пр пробросе в моем скрипте console.error(e); выводит сообщение из моего Exception-а, при этом давая ссылку на код скрипта tenshi
Цитата:

Сообщение от tenshi
найди потом имя файла и номер строки...

вообще-то весь backtrace сохраняется
вот только почему-то при вызове из моего скрипта backtrace ведет на ваш скрипт, чего вы так добивались)))
при пробросе же
console.warn(e);
console.trace();

можно уивдеть корректный backtrace

tenshi 08.02.2010 03:37

почему в ие не работает - понятно. страница в режиме совместимости видимо.

tenshi 08.02.2010 03:54

Цитата:

можно уивдеть корректный backtrace
нельзя, ибо бэктрейс и координаты будут указывать не на место возникновения исключения ( throw 'my error'), а на место, где мы его не смогли обработать ( throw e или console.error(e) )

Gvozd 08.02.2010 03:59

Цитата:

Сообщение от tenshi
страница в режиме совместимости видимо.

я никаких doctype-ов не прописывал.
старый-добрый HTML.
для своего варианта, я решил проблему с отладкой
предпоследняя строчка backtrace-а в логах указывает на контекст вызова
Exception также виден.
решение firebug-only, поэтому не годится для отладки в других браузерах, к сожалению

B~Vladi 08.02.2010 11:53

Цитата:

Сообщение от Gvozd
решение firebug-only

И ИЕ8
Ещё можно оперу прикрутить.

tenshi 08.02.2010 13:32

Цитата:

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

tenshi 08.02.2010 14:30

пофиксил оперу

PeaceCoder 08.02.2010 14:40

Вот сижу и задаюсь вопросом. Вы когда чтото пишите, Вы что пишете это всем скопом а потом запускаете в ожидании где же ошибка будет? Отлаживать код надо последовательно.

tenshi 08.02.2010 14:54

если бы я писал весь код сам - мне бы не пришлось патчить прототайп и вычищать оттуда трайкатч - я бы его просто не использовал х)

PeaceCoder 08.02.2010 15:05

Цитата:

Сообщение от tenshi
я бы его просто не использовал х)

Это умное решение. когда встал вопрос какая из ФВ лучшая, то прототайп у меня улетел сразу как я в него глянул.

tenshi 08.02.2010 16:36

а кто прилетел вместо него?

PeaceCoder 08.02.2010 21:10

Цитата:

Сообщение от tenshi
а кто прилетел вместо него?

jQ. Еще посматриваю на mootools.

tenshi 08.02.2010 23:23

хрен редьки не слаще..

Gvozd 08.02.2010 23:41

tenshi,
может попробуешь довести свой скрипт под осла?
у меня не выходит:
1)на root onpropertychange не срабатывает. зато срабатывает на document-е
2)даже если он срабатывает, то оба срабатывания происходят после вывода alert([ inverse( 0 ), inverse( 1 ) ])
то есть я не знаю, как получить res в такой ситуации

tenshi 08.02.2010 23:50

у меня в осле работало.. правда проверял я в 8..
а вот в лисице - жопа >_<" ща попробую по другому переделать..

Gvozd 08.02.2010 23:56

Хитрый план такой хитрый)))
не гарантирую, что это будет работать в 100% случаев/
var FThread= new function(){
//Version: 3
//Descr: 'creates a wrapper for function that allows you to not be afraid of exceptions in'
//License: 'public domain'

//Implementation:
var root= document.documentElement
var FThread= function( proc ){
	var thread= root.addEventListener
	? function( ){
		var res, self= this, args= arguments
		root.addEventListener( 'launch thread', function( ){
			root.removeEventListener( 'launch thread', arguments.callee, false )
			res= thread.proc.apply( self, args )
		}, false )
		var event= document.createEvent( 'UIEvents' )
        event.initEvent( 'launch thread', false, false )
        root.dispatchEvent( event )
		return res
	}
	: function( ){
		var res, self= this, args= arguments
		var image=document.createElement('img');
		image.onerror=function()
			{
			res= thread.proc.apply( self, args )
			}
		image.src='';//or 'about:blank'
		return res
	}
	thread.proc= proc
	return thread
}

//Export: return FThread

//Usage:
var inverse= FThread(function( a ){
	if( a === 0 ) throw 'division by zero'
	return 1/a
})
alert([ inverse( 0 ), inverse( 1 ) ])
// prints [ undefined, 1 ] and exception 'division by zero' in console log 

}

исключение бросается уже как в опере, так и в осле.
DebugBar даже начал отладку
а вот в лисе, исключения не пробрасываются

tenshi 09.02.2010 14:13

var FThread= new function(){

Version: 4
Descr: 'creates a wrapper for function that allows you to not be afraid of exceptions in'
License: 'public domain'
 
Implementation:

var scripts= document.getElementsByTagName( 'script' )
var script= scripts[ scripts.length - 1 ]
var starter= document.createElement( 'button' )
starter.id= 'thread starter'
starter.style.display= 'none'
script.parentNode.insertBefore( starter, script )

var FThread= function( proc ){
    var thread= function( ){
		var res, self= this, args= arguments
		starter.onclick= function( ev ){
		    ( ev || event ).cancelBubble= true
		    starter.onclick= null
		    res= thread.proc.apply( self, args )
		}
        starter.click()
        return res
    }
    thread.proc= proc
    return thread
}
 
Export: return FThread

Usage:

var inverse= FThread(function( a ){
    if( a === -1 ) throw (void 0)()
    if( a === 0 ) throw new Error( 'custom error' )
    return 1/a
})
alert([ inverse( -1 ), inverse( 0 ), inverse( 1 ) ])
// alerts ",,1" and two exceptions in console log

}

исключения всплывают везде, только вот кастомные исключения теряют бэктрейс в мозилле и вообще всё в опере

tenshi 09.02.2010 14:58

с оперой разобрался - нужно было обновить и включить: opera:config#UserPrefs|Exceptions Have Stacktrace

Gvozd 09.02.2010 18:11

Цитата:

Сообщение от tenshi
исключения всплывают везде

в мозилле твоя третья версия скрипта из первого сообщения в теме не пробрасывала ничего
вторая до правки также
теперь в мозилле пробрасывает.

теперь вообще везде работает.
красота
про click хорошо придумал. для него есть гарантия, что если на протестированном браузере сработал именно так, то так будет всегда, а не зависеть от погоды, как в случае image.onerror у меня

tenshi 09.02.2010 18:18

примечательно, что cancelBubble работает во всех браузерах

artyv 23.03.2010 12:46

Сделал DOM-независимое решение:

/**
 * Javascript ThreadBox
 *
 * DOM-independent implementation of FThread {@link http://habrahabr.ru/blogs/javascript/86852/}
 *
 * Usage: threadBox([context,] function);
 *
 * @author Vasiliy Aksyonov <outring@gmail.com>
 * @version 1.0
 */

var threadBox = (function() {

    var thread;

    try { thread = new ActiveXObject("Msxml2.XMLHTTP"); }

    catch (e) {

        try { thread = new ActiveXObject("Microsoft.XMLHTTP"); }

        catch (e) { thread = false; }
    }

    if (!thread && typeof XMLHttpRequest != 'undefined') {

        thread = new XMLHttpRequest();
    }

    if(thread) {

        var threadBox = function () {

            var context = arguments.length >= 2 ? arguments[0] : null;
            var process = arguments.length >= 2 ? arguments[1] : arguments[0];

            thread.onreadystatechange = function() {

                if (!process) {

                    return;
                }

                var innerProcess = process;

                process = null;

                innerProcess.call(context);
            };

            thread.open('GET', '#', true);
            thread.send(null);
        }
    }

    else {

        var threadBox = function () {

            var context = arguments.length >= 2 ? arguments[0] : null;
            var process = arguments.length >= 2 ? arguments[1] : arguments[0];

            try { process.call(context); }
            catch(e) { setTimeout(function() { throw e; }, 0); }
        }
    }

    return threadBox;
})();


Несмотря на то, что делаем асинхронный XMP запрос, работает вроде синхронно. Способ использования отличается конечно, но ничто не мешает переделать, но на мой взгляд (мне) так удобнее : D
Ну и да, как видно добавлен небольшой костыль внизу, на всякий случай, в хроме он конечно не сильно поможет : D но хоть что-то

tenshi 23.03.2010 12:56

ага, я попробовал с синхронным и обламался х)

tenshi 23.03.2010 13:13

решётку запрашивать - не вариант. ибо если не прокэшируется, то это жопа.
ещё у тебя несколько раз должна вызываться функция при каждом изменении состояния. поэтому надо удалять обработчик при первом использовании, как у меня.

artyv 23.03.2010 13:19

1 надо проверить, мне кажется что он пытается выполнить стандартный запрос якоря на текущей странице, как при клике на ссылку, так что тут даже вероятно и запроса то к серверу не идёт
2 выполнять 2 раза не будет, т.к. после выполнения я обnullяю process : D хотя проще было убить хэндлер, это да, затупил

Как проверить проще всего? Не хочется ставить мониторов соединений никаких

artyv 23.03.2010 13:32

Проверил синхронность. FAIL

tenshi 23.03.2010 13:38

м.. не, реквесты не катят - ошибки глотаются =(

tenshi 23.03.2010 13:39

var FThread= new function(){

Version: 5
Description: 'creates a wrapper for function that allows you to not be afraid of exceptions in'
License: 'public domain'
 
Implementation:

var FThread= function( proc ){
    var thread= function( ){
        var res, self= this, args= arguments
        var starter= new XMLHttpRequest
        starter.onreadystatechange= starter.onabort= function( ev ){
        	starter.onreadystatechange= starter.onabort= null
		    res= thread.proc.apply( self, args )
		}
        //console.dir( starter )
        starter.open( 'get', '#', true )
        starter.send( null )
        starter.abort()
        return res
    }
    thread.proc= proc
    return thread
}
 
//Export: return FThread

Usage:

var inverse= FThread(function( a ){
    if( a === -1 ) (void 0)()
    if( a === 0 ) throw Error( 'division by zero' )
    return 1/a
})
alert([ inverse( -1 ), inverse( 0 ), inverse( 1 ) ])
// alerts ",,1" and two exceptions in console log

}

artyv 23.03.2010 13:41

У меня такое наблюдается только в FF3.6, в более старых версиях ок, возможно дело в плагинах.
А с синхронностью обстоит так: в FF при синхронном запросе onreadystatechange не выполняется, а во всех остальных браузерах выполняется, но в IE всегда почему-то только один раз

artyv 23.03.2010 15:04

В общем всё решилось, кроме того, что FF>=3.5 проглатывает ошибки зачем то : (
Вот как поменялась функция:
var threadBox = function () {

    var context = arguments.length >= 2 ? arguments[0] : null;
    var process = arguments.length >= 2 ? arguments[1] : arguments[0];

    thread.open('GET', '#', Array.toSource ? true : false);

    thread.onreadystatechange = function () {

        thread.onreadystatechange = function () { };

        process.call(context);
    };

    thread.send(null);
}


Пока что ищу решение, как заставить ФФ не глотать : D


Часовой пояс GMT +3, время: 02:27.