EventEmitter event ordering
May. 1st, 2016 12:24 pmhttp://stackoverflow.com/q/36956220/1935631
It looks like the people don't even understand the meaning of the question.
Is there a safe pattern / good practice / guarantee by design on the arrival and observation of events?
It looks like the people don't even understand the meaning of the question.
Is there a safe pattern / good practice / guarantee by design on the arrival and observation of events?
$ cat a.js
'use strict';
const EventEmitter = require('events');
const e = new EventEmitter();
e.on('a', x => {
console.log('x = ' + x);
if (x === 0) {
e.emit('a', 2);
e.removeAllListeners('a');
e.emit('a', 3);
e.on('a', y => console.log('y = ' + y));
e.emit('a', 4);
}
});
e.on('a', z => console.log('z = ' + z));
console.log('a');
e.emit('a', 0);
console.log('b');
e.emit('a', 1);
console.log('c');
$ node a.js
a
x = 0
x = 2
z = 2
y = 4
z = 0
b
y = 1
c
- emit is atomic with respect to remove* and add* listeners
- emits are not atomic with respect to each other
- As a result, consumers can't agree on the observed order of events: x=0,x=2, but z=2,z=0 - but what's the way to make them agree? The only guarantee I can see is program order of emit start with respect to emit end (ie make all emits occur in a single place)
no subject
Date: 2016-05-01 03:01 pm (UTC)no subject
Date: 2016-05-01 03:43 pm (UTC)На вопрос в посте ответ такой: EventEmitter нереентрабельный, если ты удаляешь обработчик из обработчика, или вызываешь emit - то получаешь по голове unspecified behaviour. Вылазят наружу детали реализации эмиттера.
Заключи всю ветку эмитирования в process.nextTick и посмотри, должно помочь.
Т.е. if (x == 0) process.nextTick(() => { ... })
no subject
Date: 2016-05-01 04:08 pm (UTC)console.log логирует все свои аргументы, т.е. вместо плюса нужна запятая:
console.log('y =', y)
Я вообще вместо этого юзаю объектные литералы:
console.log({ y : y })
Если нужно составную строку писать - console.log умеет работать как printf, т.е. "y = %s", y
no subject
Date: 2016-05-01 05:53 pm (UTC)Да, на SO вопрос сформулирован по-другому, т.к. хотелось привязать к реалу. Вот у нас есть обработчик connect, есть обработчик error, есть обработчик timeout. Нужны гарантии, что они взаимоисключают друг друга, т.к. при каких-то раскладах я наблюдал, что error вызывался на старом списке обработчиков, а там обработчик только ошибок соединения. Не исключено, что там был хитрый случай (например, кто-то что-то хотел засунуть в логгер, логгер полез к моему сокету, а тот уже error в неподходящий момент), но хотелось как раз послушать о гарантиях от собственно EventEmitter и какие подходы считаются работающими по спецификации (хахаха), а какие подсказаны практикой.
Да, nextTick, по-видимому, должен помочь. emit в себе асинхрона не содержит, так что, можно построить synchronization order, в котором все event handlers начинаются в одном тике, и тогда process.nextTick(...) гарантированно после начала event handlers.
no subject
Date: 2016-05-01 05:54 pm (UTC)no subject
Date: 2016-05-01 05:57 pm (UTC)EventEmitter.emit - это просто handlers.forEach(x => x(args))
Соответственно чтобы всё было в порядке, достаточно делать "рекурсивные" вызовы в отдельном тике. Оно сразу полечится.
"Правильное" решение - это написать изначально устойчивый EventEmitter. Но поменять эмиттер в составе ноды не удастся, там придётся юзать этот трюк "вызывать эмит рекурсивно только на следующем тике", либо сделать декоратор.
no subject
Date: 2016-05-01 05:59 pm (UTC)no subject
Date: 2016-05-01 06:01 pm (UTC)