finally

sane flow control library

npm install finally
1 downloads in the last day
485 downloads in the last week
2 119 downloads in the last month

Finally

Finally is a simple flow control library for node.js and the browsers, with a mentally sane API.

It works like this

Think of your flow as an array of sequential operations, each one containing an array of parallel operations.

Reading 2 files sequentially

var fs = require('fs')
var flow = require('finally')

flow(function(){
  fs.readFile('path/to/file', 'utf-8', this.continue)
  // this.continue is a node-style callback that also triggers the next sequential
})
.then(function(error, data1){
  if (error) this.break(error)
  // this.break is a node-style callback that also triggers the last sequential
  fs.readFile('path/to/file', 'utf-8', function(error, data2) {
    this.continue(error, data1, data2)
  }.bind(this))
})
.finally(function(error, data1, data2){
  if (error) throw error
  console.log(data1, data2)
})

Reading 2 files in parallel

var fs = require('fs')
var flow = require('finally')

flow(
  function(){
    fs.readFile('path/to/file', 'utf-8', this.done)
    // this.done is a node-style callback that increments the parallels counter and
    // triggers the next sequential when all are completed.
    // the first argument passed to done is then distributed, in order, to the next sequential
  },
  function(){
    fs.readFile('path/to/file', 'utf-8', this.done)
  },
)
.finally(function(error, data1, data2){
  if (error) throw error
  else console.log(data1, data2)
})

Spreading arguments

var fs = require('fs')
var flow = require('finally')

flow(function(){
  fs.readFile('path/to/file.json', function(error, data){
    if (error) return this.break(error) // goes to finally
    var someObject = JSON.parse(data)
    var someArray = someObject.someArray // someArray is [0,1,2,3,4]
    this.spread(null, someArray)
    // this.spread is a node-style callback that triggers the next sequential
    // n times as the array's length
    // treating the next sequential as an array of parallels
    // each iteration gets the array[index] value as a single argument (after error, ofcourse)
  }.bind(this))
})
.then(function(error, n){
  this.done(error, n + 1)
})
.finally(function(error, n1, n2, n3, n4, n5){
  if (error) throw error
  else console.log(n1, n2, n3, n4, n5) // [1,2,3,4,5]
})

Continuing on error

Say we want to continue on error only, for instance to find the first existing file.

var fs = require('fs')
var flow = require('finally')

flow(function(){
  fs.readFile('path/to/file1', function(error, data){
    if (error) this.continue(error)
    else this.break(null, 'path/to/file1', data)
  }.bind(this))
})
.then(function(error, n){
  fs.readFile('path/to/file2', function(error, data){
    if (error) this.continue(error)
    else this.break(null, 'path/to/file2', data)
  }.bind(this))
})
.then(function(error, n){
  fs.readFile('path/to/file3', function(error, data){
    if (error) this.continue(error)
    else this.break(null, 'path/to/file3', data)
  }.bind(this))
})
.finally(function(error, path, data){
  if (error) console.log('no existing file found')
  else console.log('first existing file was ' + path + ' with data ' + data)
})

Reading files sequentially, generating the flow

var fs = require('fs')
var flow = require('finally')

var ƒ = flow()

['path/to/file1', 'path/to/file2', 'path/to/file3'].forEach(function(path) {
  ƒ.then(function(error){
    fs.readFile(path, function(error, data){
      if (error) this.continue(error)
      else this.break(null, path, data)
    }.bind(this))
  })
})

ƒ.finally(function(error, path, data){
  if (error) console.log('no existing file found')
  else console.log('first existing file was ' + path + ' with data ' + data)
})

There is also a shortcut for the above:

var fs = require('fs')
var flow = require('finally')

var ƒ = flow()

ƒ.sequential(['path/to/file1', 'path/to/file2', 'path/to/file3'], function(path, i, error) {
  fs.readFile(path, function(error, data){
    if (error) this.continue(error)
    else this.break(null, path, data)
  }.bind(this))
})

ƒ.finally(function(error, path, data){
  if (error) console.log('no existing file found')
  else console.log('first existing file was ' + path + ' with data ' + data)
})

Reading files in parallel, generating the flow

var fs = require('fs')
var flow = require('finally')

var ƒ = flow()

ƒ(['path/to/file1', 'path/to/file2', 'path/to/file3'].map(function(path) {
  return function(error){
    fs.readFile(path, function(error, data){
      if (error) this.done(error)
      else this.break(null, path, data)
    }.bind(this))
  }
})

ƒ.finally(function(error, path, data){
  if (error) console.log('no existing file found')
  else console.log('first existing file was ' + path + ' with data ' + data)
})

There is also a shortcut for the above:

var fs = require('fs')
var flow = require('finally')

var ƒ = flow()

ƒ.parallel(['path/to/file1', 'path/to/file2', 'path/to/file3'], function(path, i, error) {
  fs.readFile(path, function(error, data){
    if (error) this.done(error)
    else this.break(null, path, data)
  }.bind(this))
})

ƒ.finally(function(error, path, data){
  if (error) console.log('no existing file found')
  else console.log('first existing file was ' + path + ' with data ' + data)
})
npm loves you