1.0.0 • Published 6 years ago

callstep v1.0.0

Weekly downloads
4
License
Apache-2.0
Repository
github
Last release
6 years ago

callstep 🚶

Promise-like async control flow library using plain functions

npm install --save callstep

table of contents

concepts

Callback

Callback<Error, Value>(err : Error, value : Value) => void

a callback is a function that is called (usually asynchronously) with an error as the first and a value as the second argument.

this was the most common asynchronous pattern in Node.js code, until Promises.

guess who's back meow, callbacks! 🐈

function callback (err, value) {}

Continuable

Continuable<Error, Value>(callback : Callback<Error, Value>) => void

a continuable is a function with one argument: a callback.

a continuable is a future value that can be asynchronously resolved later, like a Promise!

function continuable (callback) {
  // do something, then call...
  callback(err, value)
}

for example:

function readConfig (callback) {
  fs.readFile('./config.json', callback)
}

Callstep

Callstep<Error, Input, Output>(value : Input) => Continuable<Error, Output>

a callstep is a function that receives an input value and returns a continuable for an output value.

function callstep (input) {
  function continuable (callback) {
    callback(err, output)
  }
}

for example:

const readConfig = readFile('./config.json')

function readFile (path) {
  return function continuable (callback) {
    fs.readFile('./config.json', callback)
  }
}

api

step = require('callstep')

step.of(value) => continuable

step.of(value : Value) => Continuable<null, Value>`

given a value, returns a continuable for the value.

source:

function of (value) {
  return callback => callback(null, value)
}

step.error(err) => continuable

step.error(err : Error) => Continuable<Error, null>`

given an error, returns a continuable for the error.

source:

function error (err) {
  return callback => callback(err)
}

step.to(asyncFn) => (...values) => continuable

convert an async function (for example fs.readFile) to a function that returns a continuable

for example:

var readFile = step.to(fs.readFile)

var readConfig = readFile('./config.json', 'utf8')

readConfig((err, text) => {
  console.log(text)
})

source:

function to (asyncFn) {
  return function (...args) {
    return function continuable (callback) {
      return asyncFn(...args, callback)
    }
  }
}

step.noop() => continuable

step.noop() => Continuable<null, null>

returns a continuable for nothing.

source:

function noop () {
  return callback => callback(null, null)
}

step.sync(syncFn) => continuable

step.sync(syncFn : () => { return value : Value | throw err : Error }) => Continuable<Error, Value>

given a synchronous function that returns a value or throws an error,

returns a continuable for the value or any caught error.

for example:

const parseJson = json => step.sync(() => JSON.parse(json))

source:

function sync (fn) {
  return callback => {
    try {
      var result = fn()
    } catch (err) {
      return callback(err)
    }
    callback(null, result)
  }
}

step.series([...continuables]) => continuable

step.series(continuables : [Continuable]) => Continuable<Error, [Result]>

given an array of continuables, returns a continuable to invoke them in order.

the callback will receive an error if one errors, or an array of results if all succeed.

uses run-series

for example:

const runSteps = step.series([stepOne, stepTwo])

step.parallel([...continuables]) => continuable

step.parallel(continuables : [Continuable]) => Continuable<Error, [Result]>

given an array of continuables, returns a continuable to invoke them in parallel.

the callback will receive an error if any error, or an array of results if all succeed.

uses run-parallel

for example:

const fetchAnimals = step.parallel([fetchCats, fetchDogs, fetchBirds])

step.waterfall([continuable, ...callsteps]) => continuable

step.waterfall([continuable, ...callsteps]) => Continuable<Error, FinalResult>

given an array of continuables return a continuable that invokes them in order, or until one errors.

uses run-waterfall

for example:

const readFile = step.to(fs.readFile)
const parseJson = json => step.sync(() => JSON.parse(json))

const readConfig = step.waterfall([
  readFile('./config.json'),
  parseJson
])

step.iff(predicate, ifTrue, ifFalse?) => callstep

step.iff(predicate : (value : Value) => Boolean, ifTrue : Callstep<Error, Value>, ifFalse : CallStep<Error, Value>) => CallStep<Error, Value>

given a predicate function that returns true or false, a ifTrue callstep if predicate is true, and an optional ifFalse callstep if predicate is false (otherwise defaults to step.of).

returns a callstep that will conditionally delegate to either ifTrue or ifFalse depending on the result of predicate.

const isString = value => typeof(value) === 'string'
const parseJson = json => step.sync(() => JSON.parse(json))

const toJson = step.iff(isString, parseJson)

step.map(source, lambda) => continuable

given a source continuable and a lambda synchronous transformation function, returns a new continuable.

the new continuable is the result of the first continuable transformed by your synchronous mapping function.

step.map<Error, Input, Output>(source : Continuable<Error, Input>, lambda : (input : Input) => { return output : Output | throw err : Error }) => Continuable<Error, Output>

for example:

const readConfig = map(
  readFile('./config.json', 'utf8'),
  JSON.parse
)

readConfig((err, config) => {
  console.log(config)
})

step.mapAsync(source, callstep) => continuable

given a source continuable and a callstep asynchronous transformation function, returns a new continuable.

the new continuable is the result of the first continuable transformed by your asynchronous mapping function.

step.map<Error, Input, Output>(source : Continuable<Error, Input>, callstep : Callstep<Error, Input, Output>) => Continuable<Error, Output>

for example:

const writeConfig = mapAsync(
  readFile('./config.json', 'utf8'),
  JSON.parse
)

readConfig((err, config) => {
  console.log(config)
})

step.swallowError(continuable) => continuable

step.swallowError(continuable : Continuable<Error, Value>) => Continuable<null, Value>

given a continuable, returns a new continuable that ignores any errors passed to the callback.

const parseJson = json => step.sync(() => JSON.parse(json))

const tryParseJson = value => step.swallowError(parseJson(value))

step.tap(fn) => callstep

step.tap(fn : (value : Value) => void) => CallStep<Error, Value>

given a tap function that receives a value, returns a callstep which runs the tap function before returning a continuable for the value.

helpful for debugging callsteps.

for example:

const readFile = step.to(fs.readFile)
const parseJson = json => step.sync(() => JSON.parse(json))

const readConfig = step.waterfall([
  readFile('./config.json'),
  step.tap(value => console.log('value', value)),
  parseJson
])

thanks

license

The Apache License

Copyright © 2018 Michael Williams

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.