Многопоточный яваскрипт
Яваскрипт - однопоточный язык программирования. однако, в нём есть поддержка отложенного выполнения, когда мы устанавливаем обработчик события timeout и выходим из программы.
При этом можно повесить несколько обработчиков, образующих конкурентную очередь выполнения.
Эта их особенность и позволяет реализовать многопоточность в текущих версиях яваскрипта.
-
Можно выполнять долгие вычисления или анимацию, не вешая при этом пользовательский интерфейс.
- Следствие из (1) - у нас нет ограничения на время выполнения.
- Каждый процесс является "истинным объектом" с очередью сообщений, рабочим пространством и независимостью от работоспособности других объектов.
из-за особеностей реализации (через таймер) суммарное время вполнения программы может сильно замедлитья, ибо setTimeout приводит к ощутимой задержке между сообщениями. однако, благодаря этому, браузер успевает отреагировать на активность пользователя и произвести все перерасчёты, связанные с ДОМ-ом, что бывает необходимо в некоторых случаях (например - native selectors в ИЕ). также мы получаем возможность производить безболезненно тяжёлые вычисления (например, сортировку большой таблицы).
что ж, ближе к делу...
Function.prototype.process= function( state ){
var process= function( ){
var args= arguments;
var self= arguments.callee;
setTimeout( function( ){
self.handler.apply( self, args );
}, 0 )
}
for( var i in state ) process[ i ]= state[ i ];
process.handler= this;
return process;
}
суть его такова: любую функцию можно превратить в процесс, вызвав её метод process. в качестве параметра ему передаётся объект, полями которого инициируется процесс. фактически, процесс является декоратором для функции, реализующиим её отложенное выполнение.
var printer= function( data ){
document.write( data + ' ' );
}
for( var i= 0; i<100; i++ ){
printer( i );
document.write( '| ' );
}
Если выполнить этот код, то будут выведены числа от 0 до 99, разделённые вертикальной чертой. то есть, выполнение нашего кода приостанавливается на время выполнения функции print.
Однако, если мы декорируем эту функцию с помощью метода process:
printer= printer.process();
for( var i= 0; i<100; i++ ){
printer( i );
document.write( '| ' );
}
, то будет выведена сначала сотня вертикальных черт, а потом уже сотня чисел. то есть, мы передали функции сообщение и не дожидаясь завершения её выполнения, работаем дальше.
но далеко не всегда нужно полностью выйти из режима выполнения полностью - зачастую требуется временно приостановить выполнение с целью последующего возобновления с того же места. для этих целей есть смысл ввести такие понятия, как "состояние процесса" и "передача сообщений". и поможет нам в реализации всего этого замечательная конструкция switch-case:
var business= function( input ){
switch( input.message ){
case 'start':
if( this.state !== 'wait' ) return this( input );
this.body= document.getElementsByTagName( 'body' )[0];
this.body.innerHTML+= '[business started] ';
this.state= 'run';
this.iteration= 0;
case 'next':
if( this.state !== 'run' ) return;
this.body.innerHTML+= this.iteration + ' ';
if( ++this.iteration < 100 ) return this({ message: 'next' });
case 'finish':
if( this.state !== 'run' ) return;
this.body.innerHTML+= '[business stopped] ';
this.state= 'wait';
return;
}
}.process({ state: 'wait' })
business({ message: 'start' });
мы создаём процесс, и инициируем его состояние строкой 'wait'. в этом режиме процесс висит и тупо ждёт комманды начать выполнение. как только к нему приходит комманда стартануть (последняя строчка), он инициализирует некоторые свои переменные ( body, iteration ), меняет состояние на 'run' и сразу переходит к выполнению первой итерации. внутри итерации выполняются некоторые действия и происходит проверка условия окончания цикла. если ещё не пора - повторяем итерацию, если пора - выполняем завершение процесса и переводим процесс в режим ожидания.
основное отличие в том, что во время выполнения процесс продолжает принимать сообщения от сторонних процессов и, например, может принять решение о досрочном завершении цикла или какого-либо иного изменения состояния. при этом в разных состояниях процесс принимает различные сообщения. например, его нельзя остановить, пока он не запущен, ибо это не имеет никакого смысла.
актуальное применение такого подхода - различные виды анимации, когда в связи с действиями пользователя требуется в срочном порядке прекратить одну анимацию и приступить к другой.
на этом пока всё. предложенные выше примеры, конечно, ещё нуждаются в доработке. например, неплохо было бы реализовать декоратор не генерирующий по анонимной функции на каждый вызов. также неплохо было бы продумать механизм разграничения внутренних сообщений и внешних. ещё нужно написать метод форканья процесса. ну и, наконец, нужно написать примеры типовых паттернов использования такой многопоточности (два из них - отложенное выполнение и цикл - уже рассмотрены).
|
тоже далеко не новая идея, но полезная и интересная
не плохая статейка. помогло
Эм. Пример с применением конструктора процессов не работает. Выводит "0" и все. Что я делаю не так?
Где можно найти рефференс по методу "process" ?
Код метода перед Вами.
Он не столь велик, чтобы для его освоения требовался отдельный мануал.