# Event Loop module {#js_event_loop} The event loop is central to event-based programming in many frameworks, and our JS subsystem is no exception. It is a good idea to familiarize yourself with the event loop first before using any of the advanced modules (e.g. GPIO and GUI). ```js let eventLoop = require("event_loop"); ``` ## Conceptualizing the event loop If you've ever written JavaScript code before, you've definitely seen callbacks. It's when a function takes another function (usually an anonymous one) as one of the arguments, which it will call later, e.g. when an event happens or when data becomes ready: ```js setTimeout(function() { console.log("Hello, World!") }, 1000); ``` Many JavaScript engines employ a queue from which the runtime fetches events as they occur, subsequently calling the corresponding callbacks. This is done in a long-running loop, hence the name "event loop". Here's the pseudocode for a typical event loop: \code{.js} while(loop_is_running()) { if(event_available_in_queue()) { let event = fetch_event_from_queue(); let callback = get_callback_associated_with(event); if(callback) callback(get_extra_data_for(event)); } else { // avoid wasting CPU time sleep_until_any_event_becomes_available(); } } \endcode Most JS runtimes enclose the event loop within themselves, so that most JS programmers don't even need to be aware of its existence. This is not the case with our JS subsystem. --- # Example This is how one would write something similar to the `setTimeout` example above: ```js // import module let eventLoop = require("event_loop"); // create an event source that will fire once 1 second after it has been created let timer = eventLoop.timer("oneshot", 1000); // subscribe a callback to the event source eventLoop.subscribe(timer, function(_subscription, _item, eventLoop) { print("Hello, World!"); eventLoop.stop(); }, eventLoop); // notice this extra argument. we'll come back to this later // run the loop until it is stopped eventLoop.run(); // the previous line will only finish executing once `.stop()` is called, hence // the following line will execute only after "Hello, World!" is printed print("Stopped"); ``` I promised you that we'll come back to the extra argument after the callback function. Our JavaScript engine does not support closures (anonymous functions that access values outside of their arguments), so we ask `subscribe` to pass an outside value (namely, `eventLoop`) as an argument to the callback so that we can access it. We can modify this extra state: ```js // this timer will fire every second let timer = eventLoop.timer("periodic", 1000); eventLoop.subscribe(timer, function(_subscription, _item, counter, eventLoop) { print("Counter is at:", counter); if(counter === 10) eventLoop.stop(); // modify the extra arguments that will be passed to us the next time return [counter + 1, eventLoop]; }, 0, eventLoop); ``` Because we have two extra arguments, if we return anything other than an array of length 2, the arguments will be kept as-is for the next call. The first two arguments that get passed to our callback are: - The subscription manager that lets us `.cancel()` our subscription. - The event item, used for events that have extra data. Timer events do not, they just produce `undefined`. --- # API reference ## run() Runs the event loop until it is stopped with `stop`.
## subscribe() Subscribes a function to an event. **Parameters** - `contract`: an event source identifier - `callback`: the function to call when the event happens - extra arguments: will be passed as extra arguments to the callback The callback will be called with at least two arguments, plus however many were passed as extra arguments to `subscribe`. The first argument is the subscription manager (the same one that `subscribe` itself returns). The second argument is the event item for events that produce extra data; the ones that don't set this to `undefined`. The callback may return an array of the same length as the count of the extra arguments to modify them for the next time that the event handler is called. Any other returns values are discarded. **Returns** A `SubscriptionManager` object: - `SubscriptionManager.cancel()`: unsubscribes the callback from the event **Warning** Each event source may only have one callback associated with it.
## stop() Stops the event loop.
## timer() Produces an event source that fires with a constant interval either once or indefinitely. **Parameters** - `mode`: either `"oneshot"` or `"periodic"` - `interval`: the timeout (for `"oneshot"`) timers or the period (for `"periodic"` timers) **Returns** A `Contract` object, as expected by `subscribe`'s first parameter.
## queue() Produces a queue that can be used to exchange messages. **Parameters** - `length`: the maximum number of items that the queue may contain **Returns** A `Queue` object: - `Queue.send(message)`: - `message`: a value of any type that will be placed at the end of the queue - `input`: a `Contract` (event source) that pops items from the front of the queue