2015-01-07 3 views
1

Существует, по-видимому, известная проблема с прикреплением обработчиков событий после того, как событие уже создано. Это проблема, в основном, при вызове функции, которые следуют образцу возвращающего EventEmitter, например:Шаблон для отложенного привязки обработчиков событий EventEmitter?

var EventEmitter = require('events').EventEmitter; 

function doSomethingAsync() { 
    var ev = new EventEmitter(), 
     something = null; 

    // Caller will never see this event because its 
    // handler is bound after fact. 

    if(!something) { 
     ev.emit('error', 'Something is null!'); 
     return ev; 
    } 

    return ev; 
} 

var res = doSomethingAsync(); 

res.on('error', function(s) { 
    console.log('Error returned: ' + s); 
}); 

Это возвратит необработанное исключение ошибок, потому что в то время error испускаются, ни одного обработчика для этого не еще прилагаются :

[email protected]:~$ node test.js 
 
events.js:87 
 
     throw Error('Uncaught, unspecified "error" event.'); 
 
      ^
 
Error: Uncaught, unspecified "error" event. 
 
    at Error (native) 
 
    at EventEmitter.emit (events.js:87:13) 
 
    at doSomethingAsync (/home/sasha/test.js:11:6) 
 
    at Object.<anonymous> (/home/sasha/test.js:18:11) 
 
    at Module._compile (module.js:460:26) 
 
    at Object.Module._extensions..js (module.js:478:10) 
 
    at Module.load (module.js:355:32) 
 
    at Function.Module._load (module.js:310:12) 
 
    at Function.Module.runMain (module.js:501:10) 
 
    at startup (node.js:124:16)

Единственное решение, я смог придумать, чтобы создать EventEmitter на вызывающей стороне , Связать обработчик заранее, и передать его функцию:

var EventEmitter = require('events').EventEmitter; 
 

 
function \t doSomethingAsync(ev) { 
 
\t var something = null; 
 

 
\t // Caller will never see this event because its 
 
\t // handler is bound after fact. 
 

 
\t if(!something) { 
 
\t \t ev.emit('error', 'Something is null!'); 
 
\t } 
 
}; 
 

 
var res = new EventEmitter(); 
 

 
res.on('error', function(s) { 
 
\t console.log('Error returned: ' + s); 
 
}); 
 

 
doSomethingAsync(res);

Это кажется безвкусным и грязным, хотя. Единственная причина, по которой обработчики событий в асинхронных операциях, которые применяются при первом методе, работает вообще, состоит в том, что для рассматриваемых операций обычно требуется больше времени, чем функция возвращает. Это дает время вызова для применения обработчиков событий к возвращенному EventEmitter.

Наверняка есть предпочтительный шаблон, идиома или скрытый бит функции JavaScript или узла, чтобы лучше обрабатывать этот случай?

Я полагаю, что один подход заключается в том, чтобы не использовать EventEmitters для передачи ошибок проверки или других ошибок, которые могут произойти мгновенно, но просто вернуть что-то еще. Но я все еще думаю, что это не решение основной проблемы; эта модель, широко представленная в документации Node и в другом месте, основывается на предположении, что операция async займет больше времени, чем время, необходимое для привязки обработчиков событий после возврата EventEmitter. В большинстве случаев это, вероятно, так, но не может быть гарантировано.

Вот почему я думаю, что должен быть лучший способ. Если этого не произойдет, это сделало бы документацию Node о наилучшем практическом использовании EventEmitters очень вводящим в заблуждение. Наверняка есть лучший способ?

+0

То, что я полагаю, было бы идеально, чтобы иметь очереди EventEmitter испускаемые события, пока слушатель для них не связан, и только испускают их тогда. –

ответ

1

Вы можете использовать process.nextTick, чтобы отменить событие, исходящее после выполнения текущего стека вызовов. Для примера:

var EventEmitter = require('events').EventEmitter; 

function doSomethingAsync() { 
    var ev = new EventEmitter(), 
     something = null; 

    // Caller will see this event because it 
    // will be emitted after current call stack 

    if(!something) { 
     process.nextTick(function() { 
      ev.emit('error', 'Something is null!'); 
     }); 
     return ev; 
    } 

    return ev; 
} 

var res = doSomethingAsync(); 

res.on('error', function(s) { 
    console.log('Error returned: ' + s); 
}); 
+0

Это именно то, что я искал, спасибо! Единственный вопрос, который у меня есть: документация process.nextTick() гласит: «В следующем цикле цикла цикла вызовите этот обратный вызов». Хотя, похоже, он работает, какова явная связь между тиками циклов событий и полной степенью стека вызовов? Другими словами, хотя функция инициализации, возможно, потребуется завершить до того, как цикл цикла повторится, что гарантирует, что последующее приложение прослушивателя событий .on() в вызывающем абоненте закончит в том же тике? Я не принимаю ничего ... –

+0

@ alex-balashov В узле v0.11.x (и будет 0.12) 'process.nextTick' реализуется через микротаску v8 (такие же, как' Promise' и 'Object.observe'). Выполняются микрозадачи ПРАВИЛЬНО ПОСЛЕ текущего выполнения текущего стека вызовов (так, другими словами, в том же тике). Поэтому, если вы используете 'process.nextTick', не беспокойтесь о итерациях цикла. На самом деле 'nextTick' очень неудачное имя. – alexpods

+0

@AlexBalashov Если вы хотите выполнить свой код в NEXT TICK, используйте 'setImmediate'. Если вы хотите просто добавить выполнение вашего кода в очередь - используйте 'setTimeout'. Посмотрите здесь http://www.youtube.com/watch?v=8aGhZQkoFbQ&index=7&list=PLEebiPdGgg6nRV77RHDNe0lXAN7ya3L_3. Это очень упрощенное объяснение цикла событий. – alexpods

Смежные вопросы