DEV Community

francesco agati
francesco agati

Posted on

Simulating a Traffic Light with Bacon.js and state machine

Handling asynchronous events and managing state effectively are essential skills. Functional Reactive Programming (FRP) provides a powerful paradigm that simplifies these tasks by treating events as continuous streams of data.

Understanding the Traffic Light State Machine

Let's start by defining the states and transitions of our traffic light using a simple state machine approach:

const Bacon = require('baconjs');

// Define traffic light states and transitions
const trafficLightStateMachine = {
  initialState: 'Green',
  transitions: {
    'Green': { nextState: 'Yellow', duration: 3000 },
    'Yellow': { nextState: 'Red', duration: 1000 },
    'Red': { nextState: 'Green', duration: 2000 }
  }
};
Enter fullscreen mode Exit fullscreen mode

Here, the traffic light begins at 'Green', transitions to 'Yellow' after 3 seconds, then to 'Red' after 1 second, and finally back to 'Green' after another 2 seconds.

Simulating Traffic Light Events

To simulate the traffic light's behavior, we'll create a stream of events using Bacon.js:

const simulateTrafficLight = Bacon.fromArray([
  { type: 'state', value: 'Green' },
  { type: 'timeout' },
  { type: 'state', value: 'Yellow' },
  { type: 'timeout' },
  { type: 'state', value: 'Red' },
  { type: 'timeout' },
  { type: 'state', value: 'Green' },
  { type: 'timeout' }
]);
Enter fullscreen mode Exit fullscreen mode

This simulateTrafficLight stream alternates between emitting state change events ('state') and timeout events ('timeout'), mimicking the traffic light's transitions in a controlled manner.

Implementing the State Machine with withStateMachine

The heart of our simulation lies in using withStateMachine provided by Bacon.js. It allows us to model the traffic light's behavior based on the defined state machine:

simulateTrafficLight
  .withStateMachine(trafficLightStateMachine.initialState, function(state, event) {
    if (event.type === 'state') {
      // Emit the current state and schedule the next transition
      return [state, [{ type: 'timeout', delay: trafficLightStateMachine.transitions[state].duration }]];
    } else if (event.type === 'timeout') {
      // Transition to the next state
      const nextState = trafficLightStateMachine.transitions[state].nextState;
      return [nextState, [{ type: 'state', value: nextState }]];
    } else {
      // Pass through unknown events
      return [state, [event]];
    }
  })
  .log();
Enter fullscreen mode Exit fullscreen mode

In this setup:

  • When a 'state' event is encountered, it emits the current state and schedules the next transition after the specified duration.
  • When a 'timeout' event occurs, it transitions to the next state as defined in the state machine.
  • Any other events are passed through without changes.

Visualizing the Traffic Light Simulation

By logging the output of our state machine, we can observe the sequence of state changes and timeouts as they occur based on our predefined simulation:

simulateTrafficLight
  .withStateMachine(/*...*/)
  .log();
Enter fullscreen mode Exit fullscreen mode

Functional Reactive Programming with Bacon.js offers a straightforward yet powerful approach to managing state and handling events in JavaScript. By using FRP principles, you can build applications that are not only responsive but also easier to maintain and extend over time.

Mastering these concepts opens up possibilities for creating more interactive and dynamic web experiences. Whether you're building a traffic light simulator or handling real-time data updates, Bacon.js and FRP provide a solid foundation for modern JavaScript development.

In conclusion, diving into FRP with Bacon.js can elevate your JavaScript skills and empower you to tackle complex event-driven scenarios with confidence.

Top comments (0)