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

generator-runner

v2.2.0

Published

like a more generic task.js

Downloads

19

Readme

generator-runner

A function scheduler to allow generators to perform asynchronous task scheduling.

require('generator-runner')(function* task(step) {
  //
  // yield to async tasks ...
  //
  return 'ret';
}, function (err, ret) {
  // err is truthy if an error was thrown
  // ret is our generator's return value
});

In order to perform async tasks we use yield $task.

require('generator-runner')(function* task(step) {
  let async_task_result = yield async_task;
});

If an async task produces an exception we throw an error at that point and we can use a try/catch block to handle asynchronous errors.

require('generator-runner')(function* task(step) {
  try {
    let async_task_result = yield async_task;
  }
  catch (e) {
    if (cant_recover(e)) throw e;
    else {
      recover_from(e);
    }
  }
}, function (e) {
  // any thrown error will stop execution and invoke this callback
});

This is more important than merely try/catch because we can achieve finally blocks for cleanup.

let file = 'test.txt';
require('generator-runner')(function* task(step) {
  let fd = open(file);
  try {
    // the life of our resource is the length of async_task_with(fd)
    yield async_task_with(fd);
  }
  // we don't need a catch
  finally {
    // this gets called regardless of success or failure
    close(fd);
  }
  
});

function runner

This performs a node style callback async function. The function is invoked with a callback to continue the generator. If the first parameter of the callback is truthy an error is thrown. Otherwise, the second value of the callback is treated as a return value.

value = yield function (next/*(err, value)*/) {
  // perform async and call next
}
  • causes an error at the point of yield if err is truthy
  • returns the value for the yield expression

disjoint function runner

This uses the step function passed into the generator to continue the generator. This is very important for evented style programming where an event listener has been setup previously and we are just waiting for an event to come in.

setTimeout(step, 0);
yield step // tell the runner to wait on step to be called
// setTimeout fires after the yield, and the generator continues

promise runner

Promises map almost directly onto generator's .next and .throw methods. Promises do not start any work since they are a data structure only; instead they will cause an error if rejected or return a value if fulfilled.

yield my_promise;
  • throw an error on rejection
  • return a value on fulfillment

generator instance running

Generator instances are a series of tasks to be performed. We can temporarily give control to another generator to perform tasks for our current generator.

This is not the same as using yield* because it produces a step guard (see below).

function* subtask() {
  yield function (next) { setTimeout(next); };
}
yield subtask();

parallel array runner

Yielding an array will result in all the values being run in parallel. The values follow all of the runners listed here. Order of values is preserved so you do not need to worry about which task ends first.

let [timeout, promise_result] = yield [
  _ => setTimeout(_, 1e3), // wait a second
  my_promise
];

parallel object runner

Similar to arrays when an object is yielded it will produce parallel tasks.

let timeouts = yield {
  timeout1: _ => setTimeout(_)
  timeout2: _ => setTimeout(_)
};

nesting

Any valid value can be placed inside of our parallel object runners; we can setup specific parallelism using nesting.

let [timeouts, promises] = yield [
  [
    _ => setTimeout(_),
    _ => setTimeout(_)
  ],
  [
    my_promise1,
    my_promise2
  ]
];

racing

Although not complex, the ability to race for whatever the first task to finish is a common work flow. The race function will let you do this. It accepts either Arrays or Objects, though it will not preserve which index was the one to finish first.

let race = require('generator-runner').race;
yield race({
  timeout: _ => setTimeout(() => _(new Error('timeout!'), 120 * 1000),
  exit: _ => child.on('exit', _),
  error: _ => child.on('error', _)
});

limited concurrency

Doing limited concurrency is actually a lot of book keeping; we provide a simple function for making your parallel tasks easier.

NOTE this only works with array style parallelism.

let concurrency = require('generator-runner').concurrent;
yield concurrency(2, [
    _ => setTimeout(_),
    _ => setTimeout(_),
    _ => setTimeout(_),
    _ => setTimeout(_),
    _ => setTimeout(_),
    _ => setTimeout(_),
    _ => setTimeout(_)
]);

step guards

While an async task or parallel task is running any attempt at disjoint stepping will result in an error to prevent race conditions.

// this will fire before our async task
setTimeout(function () {
  try {
    // this throws an error because we are not waiting on `step`
    step();
  }
  catch (e) {
    console.log(`I'm busy~`);
  }
}, 0);
yield _ => setTimeout(_, 1e3);