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

fnflow

v0.5.3

Published

Pronounced "effin' flow" because it's so badass, fnFlow is a Javascript control flow library heavily influenced by Caolan McMahon's async that encourages a proper functional design pattern.

Downloads

17

Readme

fnflow

Pronounced "effin' flow" because it's so badass, fnflow is a Javascript control flow library heavily influenced by Caolan McMahon's async that encourages a proper functional design pattern.

For a complicated series of both asynchronous and synchronous tasks, using the Flow class makes adding new tasks much easier and makes the code more readable. It also encourages you to define functions in places where they can be reused more easily. This makes it an excellent choice for design patterns like MVC where it is a goal to strive for "fat model, skinny controller."

Install

npm install fnflow

Usage

var Flow = require('fnflow').Flow, Task = Flow.Task;

var flow = new Flow({
  author: new Task(Author.getByName, 'author_name'),
  genre: new Task(Genre.getByName, 'genre_name'),
  books: new Task(Book.findByAuthorAndGenre, 'author', 'genre'),
});

flow.execute({ author_name: 'Brandon Sanderson', genre_name: 'Fantasy' }, function(err, results){
  if(err) return console.error(err);
  console.log(results.books.length + " books found.");
});

...which translates to the following workflow:

  • Get the author by name "Brandon Sanderson" and get the genre by name "Fantasy" asynchronously, in parallel.
  • Get the fantasy books in the Wheel of Time series written by Brandon Sanderson by calling Book.findByAuthorAndGenre(author, genre, callback) by using the results of the author and genre tasks.

Flow(tasks)

Constructor function. It determines the best order for running functions based on their requirements. Flow automatically determines which functions depend on other functions to complete first by examining all function arguments, and each function is run as soon as its requirements are satisfied. If any of the functions pass an error to their callback, that function will not complete (so any other functions depending on it will not run) and the main callback will be called immediately with the error. The main callback receives an object containing the results of functions which have completed so far.

Arguments

  • tasks - An object literal containing named Tasks. The key used for each function or array is used when specifying parameters or requirements to other tasks.

flow.execute(data, callback)

  • data - Either an object literal containing a set of static data, or an array of such objects. The key used for each data value is used when specifying parameters in tasks. If an array is specified, the tasks will execute in parallel for each data item in the array, and the callback will be passed an array of results.
  • callback(err, results) - A callback which is called when all the tasks have been completed, or an error has ocurred. Results will always be passed but if an error occurred, no other tasks will be performed, and the results object will only contain partial results.

Running Multple Sets of Tasks in Parallel

var flow = new Flow({
  author: new Task(Author.getByName, 'author_name'),
  books: new Task('author.getBooks'),
});

flow.execute([
  { author_name: 'Brandon Sanderson' },
  { author_name: 'Jack Vance' }
], function(err, results) {
  if(err) return console.error(err);
  results.forEach(function(result){ console.log(result.author_name + " wrote " + result.books.length); });
});

...which translates to the following workflow:

  • At the same time, asynchronously do the following:
    • Get the author by name "Brandon Sanderson," and then retrieve his books.
    • Get the author by name "Jack Vance," and then retrieve his books.

Flow(name_of_result, tasks)

Constructor function. Use this constructor for nesting Flows. Nesting Flows is useful when you want to execute a set of tasks against each item in an Array result of a parent Flow.

Arguments

  • name_of_result - A string that identifies the name of a result from a parent Flow. The value of the result will be used to execute this flow.
  • tasks - An object literal containing named Tasks. The key used for each function or array is used when specifying parameters or requirements to other tasks.

Nested Flow Example

var flow = new Flow({
  author: new Task(Author.getByName, 'author_name'),
  books: new Task('author.getBooks'),
  books_data: new Flow('books', {
    pages: new Task('books.getPages'),
    words: new Task('books.getWords')
  })
});

flow.execute({ author_name: 'Brandon Sanderson' }, function(err, results) {
  if(err) return console.error(err);
  var total_pages = 0, total_words = 0;
  results.books_data.forEach(function(book_data){ 
    total_pages += book_data.pages;
  });
  console.log(results.author_name + " wrote " + total_pages + " pages and " + total_words + " words.");
});

...which translates to the following workflow:

  • Get the author by name "Brandon Sanderson"
  • Get his books.
  • For each book, asynchronously in parallel, get the total number of pages and words at the same time.

Task(fn_or_string, [*string_args])

Constructor function. Represents an asynchronous function whose final argument is a callback function(err, value).

  • fn_or_string - An actual function object, or a string that describes a function that is an attribute of another task result.
  • string_args - Each one represents an argument value to pass to the fn_or_string. Each string must stem from a piece of given to the Flow.prototype.execute function, or a task name.

task.defaultTo(task_name)

Default the result of a task to the result of a task or data if it did not yield one (undefined or null).

Example

var flow = new Flow({
  book: new Task(Book.findByTitle, 'title'),
  author_name: new Task(Author.getNameById, 'book.author_id').defaultTo('unknown_title')
});

flow.execute({ title: 'Beowulf', unknown_title: 'unknown' }, function(err, results){
  if(err) return console.error(err); 
  console.log(results.author_name + " wrote " + results.title); //prints "unknown wrote Beowulf"
});

...which translates to the following workflow:

  • Get the book asynchronously by title.
  • After retrieving the book, get the name of the author by id. If the author name retrieved is null or undefined, set it to "unkown".

task.requires(task_names)

Explicitly specify the names of tasks that must complete prior to the execution of this task. This is useful when a requisite task does not yield a value that is meaningful to this task.

Example

var flow = new Flow({
  user: new Task(User.getByUsername, 'username'),
  current_user: new Task(User.getByUsername, 'current_username')
  checkAuthorization: new Task(user.checkAuthorization, 'current_user')
  updateUser: new Task(user.update, 'user_data').requires('checkAuthorization'),
});

flow.execute({ 
  username: "jsmith", 
  current_username: "bjoe",
  user_data: { email: '[email protected]' }
}, function(err, results){
  if(err) return console.error(err);
  console.log(results.books.length + " books found.");
});

...which translates to the following workflow:

  • Get the user 'jsmith' and the current user 'bjoe' asynchronously in parallel at the same time.
  • Check whether user 'bjoe' is authorized to update the email address of user 'jsmith'
  • Once the authorization check is complete, update the user 'jsmith'.

task.assertExists(task_names)

Exit the Flow exeuction with an error if the result of the task does not yield a value.

Example

var flow = new Flow({
  author: new Task(Author.getByName, 'author_name').assertExists(),
  books: new Task(Book.findByAuthor, 'author'),
});

flow.execute({ author_name: 'Brandon Sanderson' }, function(err, results){
  if(err) return console.error(err);  //this will be true if there was no author found
  console.log(results.books.length + " books found.");
});

...which translates to the following workflow:

  • Get the author asynchronously by name. If no author is found, exit with an error.
  • If an author was found, asynchronously retrieve the books written by the author.

Authors

This library was developed by David Fenster with major contributions from Ben Kovacevich at Shutterstock

Contribute

Please do! Check out our Contributing guidelines.

License

MIT © 2013-2017 Shutterstock Images, Inc.