Events
Asynchronous code often needs to interact with evented code. Using async/await
this can be
quite challenging. Evented code often needs to be synchronous, because the timing of when to
subscribe and unsubscribe is very critical, otherwise race conditions can occur where events
get missed. Effection has powerful tools for working with Events.
Single events
The simplest operation for working with events that Effection provides is the
once
operation. This operation blocks and waits for the event to occur. For
example, consider that we wanted to wait for the open
even on a WebSocket
,
we could do something like this:
import { main, once } from 'effection';
main(function*() {
let socket = new WebSocket('ws://localhost:1234');
yield once(socket, 'open');
console.log('socket is open!');
});
The once
operation works with both Node's EventEmitter
events which use
on/off
and the DOM EventTarget
events which use
addEventListener/removeEventListener
.
The once
operation returns the argument passed to the event handler. For
example we could use this to grab the code
that the socket closed
with:
import { main, once } from 'effection';
main(function*() {
let socket = new WebSocket('ws://localhost:1234');
yield once(socket, 'open');
console.log('socket is open!');
let closeEvent = yield once(socket, 'close');
console.log('socket closed with code', closeEvent.code);
});
Events with multiple arguments
In very rare cases, some event emitters pass multiple arguments to their event
handlers. For example the ChildProcess in NodeJS emits both a status
code and a signal to the 'exit' event. It would not be possible to read the
signal from the exit
event using just the once()
operation.
For cases like these, the onceEmit
function exists, which works the same as
once
, except it returns an array of all arguments passed to the event
handler.
Recurring events
If you've been following the chapter on streams and subscriptions, you may
already have a feeling that it is not a good idea to repeatedly call
once(socket, 'message')
to grab the messages sent to a WebSocket. The risk
here is that we miss messages if we're not very careful.
Instead we can use on
. on
is a very convenient function which takes an
event emitter or event target and the name of an event, and returns a Stream of
values.
import { main, once } from 'effection';
main(function*() {
let socket = new WebSocket('ws://localhost:1234');
yield on(socket, 'message').forEach(function*(message) {
console.log('message:', message.data);
});
});
This way we can convert events into Effection streams, and we can also use all of the Stream operations to transform these streams:
import { main, once } from 'effection';
main(function*() {
let socket = new WebSocket('ws://localhost:1234');
let messages = on(socket, 'message').map((event) => JSON.parse(event.data));
yield messages.match({ type: 'start' }).expect();
console.log('got start message!');
});