ya! library for go routines in JavaScript (Next, ES6)

ya! is a go-routine implementation in ES6 using generators and promises, inspired by task.js

A month ago I discovered Go language, a kind of modern C designed by Robert Griesemer, Rob Pike, and Ken Thompson inside Google and after passing the online tutorial I ended fascinated by go-routines and channeled communications.

In Go language a go-routine is any function launched by using the go keyword.  Unlike co-routines, a go-routine has no explicit control over its execution and it is a runtime scheduler who is in charge of pausing and resuming routines. A go-routine is a green thread ruled by the go runtime.

In Go, communications between go-routines are made explicit by using channels (the communication model is based on Communicating Sequential Processes). A channel can receive or send data. When a go-routine is sending data to a channel but nobody is waiting for that data in the same channel, the go-routine blocks until someone consumes the data. The same happens when getting data and there is no data available.

Generators in JavaScript in addition to a simple scheduler enable this kind of collaboration and ya! is a library to emulate Go go-routines related features such as go-routines themselves, channels (buffered and unbuffered) and special Go statements like select (range is coming!).

You can find the library in bower under the name of ya.js and read the docs or see the annotated source on GitHub.

As an extra, here you have the go version of the dinning philosopher problem and the JS version using ya!

'use strict';

var BITES = 3;
var philosophers = ['Aristotle', 'Kant', 'Spinoza', 'Marx', 'Russell'];
var fork = {};

// The channel is used by the philosopher to notify they finished.
var done = ya.channel();
ya.onerror = function (error) { throw error.error; };

// A simple logger.
function getLog(name) {
  return function (message) {
    console.log(name + ' says: ', message);
  }
}

// The timeout function returns a promise, waits from 1 to 3 seconds and then
// fulfill the promise. Following task.js style, ya! allows you to block on
// promise returning functions and resuming execution once promises are
// fulfilled.
function timeout() {
  var seconds = Math.floor(Math.random() * 2) + 1;
  return new Promise(function (resolve) {
    setTimeout(function () { resolve(); }, seconds * 1000);
  });
}

// Some convenient alias.
function eat() {
  return timeout();
}

function think() {
  return timeout();
}

// The philosopher is a generator so we can use `yield` inside for blocking.
function* philosopher(name, lefthand, righthand, done) {
  var log = getLog(name);
  for (var i = 0; i < BITES; i++) {

    // Once the philosopher is getting hungry, he try to get forks with
    // both hands blocking if there is no fork on one of his hands.
    log('Getting hungry (' + (i + 1) + ' of ' + BITES + ')...');
    yield lefthand.get();
    yield righthand.get();

    // After waiting for the forks, he eats. Notice we block on `eat()` as
    // well. After timeout, the routine will be resumed.
    log('Eating...');
    yield eat();

    // After eating the philosopher drops both forks, despite `yield`, these
    // lines won't block because fork channels are buffered as you'll see
    // later.
    yield righthand.send(fork);
    yield lefthand.send(fork);

    // And he thinks.
    log('Thinking...');
    yield think();
  }

  // Once he's satisfied, notify he's leaving without blocking. Notice, there
  // is no `yield`.
  log('Satisfied');
  done.send(true);
  log('Leaving the table.');
}

// The maitre is another generator in charge of sit the philosophers and set
// the table.
function* maitre() {
  var log = getLog('maitre');

  // He designates the place for the first fork. Each fork place will be
  // a channel with room for one item (the fork).
  var firstPlace = ya.channel(1);
  var leftPlace = firstPlace,
      rightPlace;

  // He puts the first fork.
  yield leftPlace.send(fork);

  // And sit all the philosophers except one, making them to share one fork.
  for (var p = 0; p < philosophers.length - 1; p++) {
    var name = philosophers[p];
    
    // For each philosopher, the maitre sets a new right fork.
    rightPlace = ya.channel(1);
    yield rightPlace.send(fork);
    ya(philosopher, name, leftPlace, rightPlace, done);

    // Finally, the right fork for this philosopher becomes the left fork for
    // the next one.
    leftPlace = rightPlace;
  }
  // The last philosopher is sat in such a way he has the first fork in his
  // right hand.
  ya(philosopher, philosophers[p], leftPlace, firstPlace, done);

  // Once all the philosophers are sat. The maitre waits for each of them.
  for (var p = 0; p < philosophers.length; p++) {
    yield done.get();
  }

  log('All philosophers are happy!');
}

ya(maitre);
Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s