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

@jalapenojs/go-channels

v0.0.49

Published

Golang channels for JS

Downloads

1

Readme

@jalapenojs/go-channels

@jalapenojs/go-channels is a library for handling asynchronous messages using channels, which are message queues. See below for more details.

Installation

yarn add @jalapenojs/go-channels

Basic Usage

import { close, go, newChannel } from "@jalapenojs/go-channels";

// create a channel
const ch = newChannel();

go(function* () {
  // put messages into the channel, which is an infinite queue
  yield ch.put("hello");
  yield ch.put("world");

  // close the channel
  close(ch);
});

go<typeof ch>(function* () {
  while (true) {
    const msg = yield ch.take();
    if (msg.done) break;
    console.log(msg.value);
  }
  // outputs:
  // "hello"
  // "world"
});

See CodeSandbox

Select

import { go, newChannel, select, InferResult } from "@jalapenojs/go-channels";

const ch1 = newChannel();
const ch2 = newChannel();

go(function* () {
  yield ch1.put("hello");
});

go(function* () {
  yield ch2.put("world");
});

type C1 = InferResult<typeof ch1>;
type C2 = InferResult<typeof ch2>;

go(function* () {
  while (true) {
    const [msg1, msg2]: [C1, C2] = yield select(ch1, ch2);
    if (msg1) {
      console.log(msg1); //`{value: hello, done: false}
    }
    if (msg2) {
      console.log(msg2); //`{value: world, done: false}
    }
  }
});

See CodeSandbox

Range

import { go, newChannel, range } from "@jalapenojs/go-channels";

const ch = newChannel<number>();

go(function* () {
  for (let i = 1; i < 10; i++) {
    yield ch.put(i);
  }
});

range(ch).forEach((msg) => {
  console.log(msg);
  if (msg === 5) {
    // return false to stop receiving messages
    return false;
  }
});

// output: 1,2,3,4,5

See CodeSandbox

Overview

What

As stated in the introduction, @jalapenojs/go-channels is a library for handling asynchronous messages.However, unlike browser events, two key differences are:

  1. Subscribers are automatically unregistered after the first event.
  2. Messages are sent to subscribers in a round-robbing fashion, based on registration order.
const ch = newChannel<number>();

go<typeof ch>(function* subscriber1() {
  while (true) console.log(yield ch.take()); //0, 2, 4, 6, 8, ...
});

go<typeof ch>(function* subscriber2() {
  while (true) console.log(yield ch.take()); //1, 3, 5, 7, ...
});

go(function* producer() {
  let len = 0;
  while (true) yield ch.put(len++);
});

See CodeSandbox

Why

To be perfectly honest, at the time (almost 5 years ago), generators seemed really cool and GoLang concurrency was way easier than anything I had worked back then (including async/await). I resurrected this project because perhaps channels can provide an easier mental model for working with asynchronous events than React hooks 😄.

The inspiration comes from GoLang Channels and redux-saga.

GoLang Channels are +30 year technology for concurrency that provides a simpler mental model than standard concurrency patterns. See the Wikipedia Article for more details.

Redux Integration

Use:

React Integration

Coming soon!

Gotchas and Limitations

In no particular order:

No deadlock-detection support 😢

GoLang tells you when there is deadlock. That would be really cool to add but I'm not even sure if it's possible.

You can't yield inside a callback

Fortunately, the following will not compile:

const elem = //... some DOM element
const ch = newChannel()
elem.addEventListener('mouseup', function() {
  yield ch.put('mouseup'); // compile error
});

Instead you should use an async version of put (which can be a good idea since blocking UI events doesn't really make sense).

elem.addEventListener("mouseup", function () {
  ch.asyncPut("mouseup"); // this works!
});

This common golang synchronization pattern won't work.

function main() {
  const messages = newChannel();
  go(function* () {
    yield messages.put("ping");
  });
  // The desired behavior is to stop execution until a message
  // is received and exit *after* the ping
  const { value: msg } = messages.take();
  console.log(msg);
  // unfortunately, this never prints ping and
  // always exits immediately 😞
}

The reason is because Javascript is synchronous. (And also because you have to yield the take inside a generator for it to have an effect.)

However, the following will work just fine. And by "fine", we mean that even though main finishes before the generators execute, the generators will still print out the ping.

function main() {
  const messages = newChannel();
  go(function* () {
    yield messages.put("ping");
  });
  go<typeof messages>(function* () {
    const { value: msg } = yield messages.take();
    console.log(msg); // prints "ping"
  });
}

Don't forget to yield

Can you spot the bug?

const output = newChannel();
const input = newChannel();
go(function* () {
  output.put("out");
  const { value: msg } = yield input.take();
});

To make put/take work, you need to yield inside of a "go" routine. As is, this code will run but silently fail. Currently, the only workaround is to write a custom eslint rule that aggressively checks for take/put usage.

No asynchronous generator support (coming soon!)

In go, the code below is valid.

ch := make(chan int)
go func() {
	ch <- 0
	time.Sleep(1*time.Second)
	ch <- 1
	close(ch)
}()

This library does not (yet) support asynchronous generators, and so you can do the following:

const ch = newChannel();
go(function* () {
  yield ch.put(0);
  setTimeout(() => {
    ch.asyncPut(1);
    close(ch);
  }, 1000);
});

Roadmap

  • asynchronous generator support
  • React support 🚀
  • An eslint plugin for detect missing yields.
  • for-of support for range.