npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fsm-async

v2.2.0

Published

Simple asynchronous state machine

Downloads

234

Readme

fsm-async

Build Status GitHub license Semver GitHub Releases GitHub Issues

A state machine implementation featuring:

  1. on<state> life-cycle events, allowing the triggering of further (internal) events during the callback.

  2. async event functions that can be awaited. Depending on the implemented logic, multiple state changes can be awaited.

  3. Generic and awaitable waitUntilStateEnters(<state>) and waitUntilStateLeaves(<state>) functions providing full flexibility to state machine clients business logic.

Example

Define the transition table as a json object,

const transitionTable = {
  initial: 'disconnected',
  transitions: [
    { ev: 'connect', from: 'disconnected', to: 'connecting' },
    { ev: '_connectDone', from: 'connecting', to: 'connected' },
    { ev: 'disconnect', from: 'connected', to: 'disconnecting' },
    { ev: '_disconnectDone', from: 'disconnecting', to: 'disconnected' }
  ]
}

then apply this logic to your object:

const StateMachine = require('fsm-async')

class MyClient extends StateMachine {

  constructor () {
    const transitionTable = {
      initial: 'disconnected',
      transitions: [
        { ev: 'connect', from: 'disconnected', to: 'connecting' },
        { ev: '_connectDone', from: 'connecting', to: 'connected' },
        { ev: 'disconnect', from: 'connected', to: 'disconnecting' },
        { ev: '_disconnectDone', from: 'disconnecting', to: 'disconnected' }
      ]
    }
    super(transitionTable)
  }
}

This injects the events as proper callable functions to your instance, hence you can write:

myClient = new MyClient()
myClient.connect()

In the body of your class you can define life-cycle functions on<event> and on<state>, which are automatically called and can be used to trigger further events:

const StateMachine = require('fsm-async')

class MyClient extends StateMachine {

  constructor () {
    const transitionTable = {
      initial: 'disconnected',
      transitions: [
        { ev: 'connect', from: 'disconnected', to: 'connecting' },
        { ev: '_connectDone', from: 'connecting', to: 'connected' },
        { ev: 'disconnect', from: 'connected', to: 'disconnecting' },
        { ev: '_disconnectDone', from: 'disconnecting', to: 'disconnected' }
      ]
    }
    super(transitionTable)
  }

  // Use async here to be able to await internally
  async onConnecting () {
    // Simulate connection establishment
    await new Promise(resolve => setTimeout(resolve, 1000))
    // Internally trigger an event bringing the machine to connected state
    this._connectDone()
  }

  async onDisconnecting () {
    // Simulate disconnection
    await new Promise(resolve => setTimeout(resolve, 1000))
    // Internally trigger an event bringing the machine to disconnected state
    this._disconnectDone()
  }
}

Now, outer code can await the connect() of your client and/or use other utility functions injected by the StateMachine. The utility functions are:

  1. getState() returns current state
  2. waitUntilStateEnters(<state>) waits until a given state is entered
  3. waitUntilStateLeaves(<state>) waits until a given state is left
  4. onStateChange(<callback(state)>) notifies about state changes
  5. onInvalidTransition(<callback(event, state)>) notifies about invalid transitions

The StateMachine class at the same time is an event emitter. Hence,

stateMachine.on('state', <callback(state)>)
stateMachine.on('invalidTransition', <callback(event, state)>)

is also possible.

Please see the provided example code (examples folder) for more details and usage patterns.

You can run the example code via:

npm run example