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

channel-event

v3.4.0

Published

A simple and highly extensible javascript event channel and control flow library. It can run generator functions, which allows async data flows, simplifies cross component communication and hoists control flows to the parent scope. This allows creating hi

Downloads

85

Readme

channel-event

A simple and highly extensible javascript event channel and control flow library. It can run generator functions, which allows async data flows, simplifies cross component communication and hoists control flows to the parent scope. This allows creating highly modular and decoupled architectures.

Events are scoped to hub instances which means that the scale of the event channels can be controlled and event collisions are non existent. This makes for easy debugging.

Bundlephobia gzip + minified

Typescript typings are built in, however it will still work in vanilla javascript

Installation

yarn add channel-event

npn install channel-event

Use

First, create a hub

import { createHub } from "channel-event";

const hub = createHub({ enableLogging: process.env.NODE_ENV === "development" });

Then, create a channel. All channels created will be able to communicate with all other channels created by the same hub. Channels created by different hubs will not be able to communicate. If a channel is created with an id, that channel may return values from listen and those values will be returned to the sender channel in a dictionary { [channelId]: value }. Multiple channels can return values to a single sender, as long as their ids differ.

const channel = hub.newChannel();
const channel2 = hub.newChannel("channel id");

channel2.listen("test", data => {
   console.log(data); // -> { type: "test", payload: 10 }
   // since this channel was created with an id passed to newChannel
   // we can return a value here
   return true;
});

const result = channel.send("test", 10);
console.log(result); // -> { "channel id": true }

Generator functions allow for reusable async logic. See Generator actions for more generator actions.

function* test(): IterableIterator<EventIterable> {
   for (let count = 0; count < 10; count++) {
      // `put` is the equivelent of channel.send, called on the current channel
      yield put("test", count);
      // sleep for 200 ms
      yield delay(200);
   }
}

function* check(): IterableIterator<EventIterable> {
   for (let index = 0; index < 10; index++) {
      // `take` blocks until the specified event is 
      // sent within the context of the current hub
      const count = yield take("test");
      console.log(count);
   }
   console.log("done!");
}

// regular listen will pick up events sent from `put`
channel.listen("test", data => {
   console.log(data); // -> { type: "test", payload: <value of count> }
});

// this will print out the numbers 0-9 with 200 ms delays between prints
channel.generator
   .addGenerator(check)
   .addGenerator(test)
   .restartOnAsyncError()
   .run();

Call dispose when finished using

channel.dispose();
// or
hub.dispose(); // calls dispose on every channel inside the hub

Promises can also be yielded and the function will block until their completion.

const promResult = yield promiseObject;

Event middleware

Middleware can be added to the event chain to change the fundamental behaviour of events. Event middleware is added to the hub with addEventMiddleware. See https://github.com/zpxp/channel-store for an example of event middleware.

hub.addEventMiddleware((context, next) => {
   // log all events
   console.log(context.type);
   return next(context);
})

hub.addEventMiddleware((context, next) => {
   next(context);
   // override the return value from listens
   // channel.send(...) will now always return 42
   return 42;
})

Generator middleware

New generator actions can be defined by calling IHub.addGlobalGeneratorMiddleware or static IHub.addGlobalGeneratorMiddleware. The static function will add the new middleware to all future instances of IHub, while the instance function will only add the middleware to that hub instance. Whenever a generator yields an EventIterable, the hub will look for any middleware whos function name matches the EventIterable.function.

Generator middleware takes 2 arguments, the first contains all the arguments that the yielded function was called with, the second is the IChannel instance. Middleware must return a Promise, that when resolved, will return the resolved data from the yield statement.

export function pow(power: number): EventIterable {
   return {
      function: "power",
      value: { power: power }
   };
}


hub.addGeneratorMiddleware("power", function(data: EventIterable<{ power: number }>, channel: IChannel): Promise<any> {
   return Promise.resolve(Math.pow(42, data.value.power));
});

// the action can now be used like this
channel.runGenerator(function*(): IterableIterator<EventIterable> {
   const num = yield pow(2);
   console.log(num); // -> 1764
});

NOTE: If the promised returned from the generator middleware implementation is pending, it is a good idea to add the reject function to the channel.onDispose function to prevent hanging promises

hub.addGeneratorMiddleware("take", function(data: EventIterable<string | string[]>, channel: IChannel): Promise<any> {
   return new Promise((resolve, reject) => {
      const unsub = channel.listen(data.value, result => {
         unsub();
         resolve(result);
      });
      // call reject when channel is disposed
      channel.onDispose(reject);
   });
});

You may also create a new channel that exists for the scope of the generator action to make disposing all listeners easy, such as in this race action implentation

hub.addGeneratorMiddleware("race", function (data: EventIterable<Array<EventIterable | Promise<any>>>, channel): Promise<any> {
   // create an isolated channel that will be disposed on completion
   // all listeners that did not win the race need to be rejected
   // and cleaned up to prevent mem leaks
   const chan = channel.hub.newChannel();

   return Promise.race(
      data.value.map(item => {
         if (item instanceof Promise) {
            return item;
         } else {
            // is iterator. yield it and see what happens
            return new Promise((resolve, reject) => {
               chan.runGenerator(function*() {
                  return yield item;
               }, resolve);
               chan.onDispose(reject);
            });
         }
      })
   ).finally(() => {
      // when one action wins the race, dispose the channel 
      // cleaning up all pending listeners/generators
      chan.dispose();
   });
});

Generator actions

Current available actions implemented in all hub instances

/**
 * waits until the dispatch of a specified type then returns the data
 * @param type the string type or types to wait on
 */
export declare function take(type: string | string[]): EventIterable;

/**
 * Calls `channel.send` on the current hub
 * @param type action type
 * @param data optional data to send to all listeners
 */
export declare function put(type: string, data?: any): EventIterable;

/**
 * Calls an async func (promise) or generator func and waits until its completion, returning
 * the result
 * @param func async func (promise) or generator function
 * @param args
 */
export declare function call<A extends any[]>(func: (...args: A) => any, ...args: A): EventIterable;

/**
 * Same as `call` except does not block until the function returns. Returns a cancel function that will cancel the forked task
 * @param func plain function, async func (promise) or generator function
 * @param args
 */
export declare function fork<A extends any[]>(func: (...args: A) => any, ...args: A): EventIterable;

/**
 * Blocks for the given ms duration
 * @param durationMs
 */
export declare function delay(durationMs: number): EventIterable;

/**
 * Call `func` whenever `type` is dispatched. Cancels any existing instances `func` that may be running
 * @param type Call func whenever this type is dispatched
 * @param func
 */
export declare function takeLatest(type: string | string[], func: (data?: any) => IterableIterator<EventIterable>): EventIterable;

/**
 * Call `func` whenever `type` is dispatched.
 * @param type Call func whenever this type is dispatched
 * @param func
 */
export declare function takeEvery(type: string | string[], func: (data?: any) => IterableIterator<EventIterable>): EventIterable;

/**
 * Call `func` whenever `type` is dispatched if no instances of `func` are running
 * @param type Call func whenever this type is dispatched
 * @param func
 */
export declare function takeLast(type: string | string[], func: (data?: any) => IterableIterator<EventIterable>): EventIterable;

Extensions, middleware

Library | Description --- | --- channel-store | An event middleware that creates an ambient state for channel hubs that can be accessed anywhere that a channel exists react-channel-event | A react provider and HoC wrapper for channel-event react-channel-store | A react provider and HoC wrapper for channel-store