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

js-go-channels

v0.0.31

Published

Golang channels for JS

Downloads

5

Readme

js-go-channels

Installation

npm install js-go-channels

Usage

If you want to try out js-go-channels, check out this REPL.

Generators, oh my!

Your app will need to support generators to use this library. Node.js >= 6 supports this out of the box. Modern desktop browsers as well. However, if you need to support mobile, legacy browsers, or legacy Node.js, then you'll need to use Babel to transpile your code. Please see the appendix.

Examples

Basic Usage

import {go, newChannel, close, select, range} from 'js-go-channels';

const ch = newChannel();

go(function*() {
  yield ch.put('hello');
  close(ch);
  // this will throw an error because you can't put on a closed
  // channel
  yield ch.put('world');
});

go(function*() {
  const msg1 = yield ch.take();
  console.log(msg1); // {value: hello, done: false}
  const msg2 = yield ch.take();
  console.log(msg2); // {value: undefined, done: true}
  const msg3 = yield ch.take();
  console.log(msg3); // {value: undefined, done: true}
});

Select

import {go, newChannel, close, select, range} from 'js-go-channels';

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

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

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

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

Range

import {go, newChannel, close, select, range} from 'js-go-channels';

const ch = newChannel();

go(function*() {
  for(let i=0; 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

Redux Integration

Use redux-thunk, redux-saga, or redux-go-workflows.

Gotchas

Javascript doesn't have multi-return types

In go, you can write the following

ch := make(chan string)
go func() {
	val := <-ch
	val, more := <- ch
}()

JS is a saner language in this regard (words I never thought I would write). Instead js-go-channel channels return objects of type {value, done}. You can use destructuring and renaming to get the same result.

const ch = newChannel()
go(function*() {
  const {value: msg1} = yield ch.take() //ignore done
  console.log(msg1)
  const {value: msg2, done} = yield ch.take()
  if (!done) {
    console.log(msg2)
  }
})

You can't yield inside a callback

Can you spot the bug?

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

If you're using Babel, the above code won't even compile. Instead you should use an async version of put (which can be a good idea since blocking UI events doesn't really make sense). Under the hood, asyncPut uses an infinite buffer.

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

The common golang synchronization pattern won't work.

func main() {
  const messages = newChannel()
  go(function* () { yield messages.put("ping") })
  const {value: msg} = messages.take()
  console.log(msg)
}

The reason has to do with the way Javascript concurrency works. Recall, it works by dispatching functions to an async queue. Whatever function is currently executing will hog the CPU. That means the take will always synchronously fire before the put, and to make it block we have to use a callback.

So the following will work just fine. And by "fine", we mean that main won't finish until it receives a "ping".

func main() {
  const messages = newChannel()
  go(function* () { yield messages.put("ping") })
  go(function* () {
    const {value: msg} = yield messages.take() 
    console.log(msg)
  })
}

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 use types or write a custom eslint rule that aggressively checks for take/put usage.

for-of loops don't (yet) support range

In go, the code below is valid. That is, range converts a channel to an asynchronous iterator and then the for loop can iterate over it. In Javascript, for-of loops do not yet support asynchronous iterators. Instead we use a custom forEach.

ch := make(chan int)
go func() {
	ch <- 0
	time.Sleep(1*time.Second)
	ch <- 1
	close(ch)
}()
for x := range ch {
	fmt.Println(x)
}
// output: 0, 1

JS version:

const ch = newChannel()
go(function*() {
  yield ch.put(0)
  // recall we have to use asyncPut inside of a callback
  setTimeout(() => ch.asyncPut(1), 1000) 
})
range(ch).forEach(x => {
  console.log(x)
})

Appendix

Legacy Browser support

babel-preset-env makes it super easy to support legacy browsers. The following shows a sample .babelrc that compiles for IE 11:

{
  "legacy": {
    "presets": [
      [
        "env",
        {
          "targets": {
            "browsers": ["ie >= 11"]
          },
          "spec": true,
          "modules": "commonJS",
          "useBuiltIns": "usage"
        }
      ]
    ],
    "plugins": []
  }
}

You can then need to add babel-polyfill as a dependency in the package.json.

That's it! (You then need to configure webpack or equivalent to use Babel.)

Note that babel-preset-env replaces the old babel-preset-es2015 plugin.