Home

Awesome

Faithful NPM version Dependency Status Build Status

Like Async, but employing promises.

Collection functions

Flow-control functions

Since a promise cannot be fulfilled with multiple values, and regular chaining is already well taken care of by chaining .then calls, there is no equivalent to async.waterfall. If you have a need for using multiple results from past functions inside a new function, I suggest to take a look at Stepthrough.

Utility functions

Usage

Faithful mimics the Async API, with three important differences:

Note about CoffeeScript

All usage examples below are written in CoffeeScript, but the code made available through the package is pure JavaScript, and works perfectly fine with JavasScript code. If you are unfamiliar with CoffeeScript syntax, you could copy and paste the code examples into the CoffeeScript compiler demo at CoffeeScript.org . When you're there, click "Try Coffeescript" in the navigation bar.

faithful.each

faithful.each(inputs, iterator)
  .then ->
    console.log "Everything has executed."
  .then null, (error) ->
    console.error error

faithful.eachSeries works the same, but ensures the iterator is not called with the next argument until the promise returned by the previous iterator is fulfilled.

each and eachSeries are also available as forEach and forEachSeries, respectively.

faithful.map

faithful.map(inputs, iterator)
  .then (outputs) ->
    console.log outputs
  .then null, (error) ->
    console.error error

faithful.mapSeries works the same, but ensures the iterator is not called with the next argument until the promise returned by the previous iterator is fulfilled.

faithful.reduce

The iterator for reduce works a little differently than the regular iterators. In line with Array.reduce and Async.reduce, the iterator takes the current reduction as its first argument, and the current input value to be processed as the second. The returned promise must resolve with the new value for the reduction.

On top of that, faithful.reduce takes an extra argument (in the middle). This specifies the initial value of the reduction.

The example below is a pretty involved way of computing the factorial of 4 (i.e. 4 * 3 * 2 * 1). Note that in this code, it's actually computed as 1 * 1 * 2 * 3 * 4. The first 1 is the initial value passed.

iterator = (reduction, value) ->
  faithful.makePromise (cb) ->
    setImmediate ->
      cb null, reduction * value
faithful.reduce([1,2,3,4], 1, iterator)
  .then (reduction) ->
    console.log reduction # 4! == 24
  .then null, (error) ->
    console.error error

By necessity, faithful.reduce does its processing serially. The value of the reduction after a particular step i must be known before the next step can be executed. When possible, it's advisable to first get an array of values in a parallel fashion (for example by employing faithful.map) and then calling reduce on the resulting array.

faithful.detect

faithful.detect(inputs, iterator)
  .then (firstMatchingInput) ->
    if firstMatchingInput?
      console.log firstMatchingInput
    else
      console.log "No input matched the criteria."
  .then null, (error) ->
    console.error error

faithful.detect gives as result the first input value for which the promises returned by the iterator was fulfilled with a truthy value (i.e. something that evalates to true in context of an if-statement). If no input value matched the criteria set by the iterator, then the result will be undefined.

Because faithful.detect starts with calling the iterator once for each value in the inputs array - before any of the promises returned have been fulfilled -, the result you'll get from detect will not necessarily be the first value inside the input array that matches the criteria set by the iterator. Rather, it's the result for which the promise returned by the iterator happened to be fulfilled first. Because of that, you may want to use detectSeries so that inputs are checked one by one, in order. With detectSeries you'll always get back the first among the inputs that matched the criteria.

faithful.applyEach - different from async.applyEach

faithful.applyEach takes as first argument an array of argument arrays, and as second argument the iterator function you want to apply these arguments to. This is very handy for typical jobs like renaming files with fs.rename or writing files with fs.writeFile. The value for this for the iterator will be an empty object.

fs = require "fs"
faithful = require "faithful"
renameFile = faithful.adapt fs.rename
renamePairs = [["a","A"],["b","B"],["c","C"]]
faithful.applyEach(renamePairs, rename)
  .then ->
    console.log "Done renaming files."
  .then null, (err) ->
    console.error err

The functionality of Async's applyEach is offered as faithful.applyToEach.

faithful.log

faithful.log logs the fulfillment value of the promise using console.log, and the failure value with console.error otherwise.

faithful.log faithful.return "abc"
# shown on console: "abc"

faithtful.dir

faithful.dir logs the fulfillment value of the promise using console.dir, and the failure value with console.error otherwise.

faithful.dir faithful.return abc:123
# shown on console: "{ abc: 123 }"

Under the hood

All collection functions of Faithful are powered by two functions that do most of the work: faithful.each and faithful.eachSeries.

Both faithful.each and faithful.eachSeries take the following arguments: values, iterator and an optional options object. This options object allows you to configure the iteration process. Look at it as a configurable loop. All options are optional.

Use with caution: The specifics of the options object may well change in the future. However, I think it's too useful not to share.

Implementation of faithful.reduce

To get a sense of how you can build something quickly which is not implemented by Faithful yet, take a look at how the Faithful.reduce is implemented.

faithful.reduce = (values, reduction, iterator) ->
  faithful.eachSeries values, ((value) -> iterator reduction, value),
    handleResult: (result) -> reduction = result
    getFinalValue: -> reduction

Things of note

Implementation of faithful.detect

faithful.detect = (values, iterator) ->
  found = false
  foundValue = undefined
  faithful.each values, iterator,
    handleResult: (result, index) ->
      return unless result # did iterator find a match?
      foundValue = values[index] # look up the value that made the iterator match
      found = true # remember that we found something
    getFinalValue: -> foundValue # fulfill promise with the found value
    stopEarly: -> found # stop the processing when we found something

Things of note

Credits

The initial structure of this module was generated by Jumpstart, using the Jumpstart Black Coffee template.

Async by Caolan McMahon is of course the big inspiration for this project. It's an absolute work horse, and it does its job without fuzz.

License

Faithful is released under the MIT License.
Copyright (c) 2017 Braveg1rl