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

@lume/eventful

v0.3.2

Published

Emit and subscribe to events.

Downloads

71

Readme

@lume/eventful

Emit and subscribe to events.

This is a standard event listener pattern, similar to EventEmitter in Node.js where objects have a .on('event-name') method, or EventTarget in the web where HTML elements have a .addEventListener('event-name') method, for listening to events from such objects.

Additionally, the Eventful class provided by this package is a mixin. It can be added onto any existing class without modifying its class hierarchy. More on that below.

npm install @lume/eventful

Usage

Working with events

@lume/eventful provides a class (or mixin) called Eventful that your objects can extend from so that they can emit events and other code can subscribe to those events.

import {Eventful} from '@lume/eventful'

// Here we define a Dog class that extends from `Eventful()` so that it can emit
// a "hungry" event any time its `makeHungry()` method is called.
class Dog extends Eventful() {
	makeHungry() {
		// Emit a "hungry" event when this method is called.
		this.emit('hungry')
	}

	feed(food) {
		// ... dog eats food (use imagination here) ...
	}
}

const dog = new Dog()

function handleHungryDog() {
	dog.feed('chow')
}

// When the dog emits the "hungry" event, let's feed it. We pass in the
// handleHungryDog function to be called any time the dog emits the "hungry"
// event.
dog.on('hungry', handleHungryDog)

// This triggers the "hungry" event, which causes the callback passed to
// `dog.on('hungry')` to fire.
dog.makeHungry()

// We can stop listening for "hungry" events by using `.off()` to remove our
// event handler, the handleHungryDog function.
dog.off('hungry', handleHungryDog)

// This time we trigger the "hungry" event again, but our handleHungryDog
// function will not run again (poor dog, don't do this to your dog in real
// life).
dog.makeHungry()

That's basically it. See also Eventful.test.ts to get more of an idea.

Using Eventful as a mixin

Did you notice the () in extends Eventful() above? That's because Eventful is a mixin.

By default, it returns an Eventful class that extends from Object. Pass in a custom base class to extend from anything you want.

As an example, imagine you already have a class defined somewhere that extends from an existing base class.

import Animal from 'somewhere'

// Imagine we have this existing Cat class.
class Cat extends Animal {
	isFeelingCurious = false

	goExploring() {
		if (this.isFeelingCurious) {
			// ... cat goes exploring (use imagination here) ...
		} else {
			// ... cat sleeps on your fluffy pillow ...
		}
	}
}

const cat = new Cat()
cat.isFeelingCurious = true
cat.goExploring()

Let's say we're updating our app, and we wish to add Eventful functionality to this class. Without a mixin, our only option would be to find the base-most class of the Cat (whether that's Animal, or something even lower like Organism), and we'd have to make that base-most class extend from Eventful (if it weren't a mixin).

But Eventful is a mixin! We can simply add it to our class, without touching anything else! Like so:

import {Eventful} from '@lume/eventful'
import Animal from 'somewhere'

// Now our class extends from both Eventful and Animal. Convenient! No
// base-class refactoring needed!
class Cat extends Eventful(Animal) {
	#isFeelingCurious = false // Let's make this private now.

	goExploring() {
		if (this.#isFeelingCurious) {
			// ... cat goes exploring (use imagination here) ...
		} else {
			// ... cat sleeps on your fluffy pillow ...
		}
	}

	// Let us now add a public getter/setter that sets our state, but also emits an event:
	set isFeelingCurious(val) {
		this.#isFeelingCurious = val
		this.emit('curiosity-changed') // We have the ability to emit events now.
	}
	get isFeelingCurious() {
		return this.#isFeelingCurious
	}
}

const cat = new Cat()
cat.isFeelingCurious = true // Setting this emits the "curiosity-changed" event.

// Define a function to run when `isFeelingCurious` changes (i.e. when a "curiosity-changed" event is emitted)
cat.on('curiosity-changed', () => {
	cat.goExploring()
})

cat.isFeelingCurious = true // Causes the event to be emitted, which makes the cat go exploring.

Why?

Why do you want to make your Cat class a little more complicated to do the same thing?

The answer is, it isn't about you! Events are useful for letting other people (or other parts of your program, written by you or other people) to react to changes (events).

The above Cat class is more practical when it another piece of code has multiple instances of it, and needs to react in differing way depending on which events happen.

For example, let's imagine that we give a Cat instance to some other piece of code by exporting it for sake of example:

class Cat extends Eventful(Animal) {
	// ... same as before ...
}

export const cat = new Cat()

// At random points in time, make the cat randomly curious or not:
setTimeout(function updateCuriousityOverTime() {
	cat.isFeelingCurious = Math.random() > 0.5

	// Keep updating every random interval of up to 1 second:
	setTimeout(updateCuriousityOverTime, Math.random() * 1000)
})

Now, some other code can decide, independently of the code that defined the cat, what to do whenever the cat's curiosity has changed, and the original code that defined the cat is not responsible for reacting to the change:

import {cat} from './cat.js'

// Maybe we're making a game (use imagination here). Let's react to cat curiosity changes:
cat.on('curiosity-changed', () => {
	if (cat.isFeelingCurious) {
		// Perhaps make a set of `Rat` and `Mouse` instances start to frantically go hide.
	} else {
		// Make the `Rat` and `Mouse` instances relax, the cat has gone to lay on a pillow.
	}
})