baselib

0.1.0 • Public • Published

baselib 0.0.x

One-stop solution for essential utilities (i.e. async loops, conditions, pub/sub) for nodejs and the browser.

NPM

N/B: The code/examples in this file are in coffee-script. Javascript examples are coming soon.

Installation (NodeJS)

npm install baselib --save

Require (NodeJS)

{
  delay
  setImmediate
  AsyncCondition
  asyncIf
  AsyncIterator
  asyncWhile
  asyncForIn
  asyncForOf
  AsyncCollector
  Publisher
  shallowCopy
  deepCopy
  once
  merge
} = require 'baselib'

Installation (Browser)

Download the latest build and put it in your application.

<script type="text/javascript" src="baselib-0.1.0.js"></script>

Features

delay

delay timeToWaitInMilliseconds, functionToCall

Example:

console.log 'Do Something'
delay 2000, ->
  console.log 'Do something after 2 seconds'

setImmediate

setImmediate functionToCall, [argument1, [argument2, ... , [argumentN]]]

Example:

setImmediate ->
  console.log "I'll be executed second"
setImmediate (someValue)->
  console.log "I'll be executed third and here is #{someValue}"
console.log "I'll be executed first"

AsyncCondition

AsyncCondition class enables you to manage your asynchronous code much more effectively while providing a reusable structure.

new AsyncCondition

Returns a new AsyncCondition object. It's methods are chainable. So you don't have to name the object.

AsyncCondition#eval expression

the eval method takes any value which is immediately evaluated to find out whether it's truthy or falsy.

AsyncCondition#then functionToCall

the then method takes a function as a parameter. The provided function is invoked if the value provided to eval is truthy. functionToCall will receive a single parameter which is a function. Call it to signal the end of operation.

AsyncCondition#else functionToCall

the else method takes a function as a parameter. The provided function is invoked if the value provided to eval is falsy. functionToCall will receive a single parameter which is a function. Call it to signal the end of operation.

AsyncCondition#finally functionToCall

the finally() method takes a function as a parameter. The provided function is invoked only after the operation of either then() or else() has been finished.

Example:

c1 = new AsyncCondition
c1.eval (typeof 1 is 'number')
c1.then (cbfn)->
  console.log 'Got a number'
  delay 100, ->
    console.log 'Delayed for the sake of example'
    cbfn() # signal the end of operation
c1.else (cbfn)->
  console.log 'Did not get a number'
  cbfn()
c1.finally ->
  console.log 'I will be called anyway when the `cbfn` of `then` is invoked'

asyncIf

A shorthand for AsyncCondition

asyncIf expression (returns an AsyncCondition instance who's eval() has already been called.)

Example:

asyncIf (typeof 1 is 'number')
.then (cbfn)->
  console.log 'Got a number'
  delay 100, ->
    console.log 'Delayed for the sake of example'
    cbfn() # signal the end of operation
.else (cbfn)->
  console.log 'Did not get a number'
  cbfn()
.finally ->
  console.log 'I will be called anyway when the `cbfn` of `then` is invoked'

AsyncIterator

AsyncIterator class provides a low level interface for running asynchronous operations in loops. It is very very generalized and so it can be used to implement almost any kind of looping behavior with minimal effort. For example, baselib comes with three functions that can effectively replace the synchronous counterparts. Namely, asyncWhile replacing while, asyncForIn replacing for ... in ... (array iteration), asyncForOf replacing for ... of ... (object's key/value pair iteration). All these functions are based on AsyncIterator.

N/B: AsyncIterator class is designed for maximum configurability. asyncWhile, asyncForOf and asyncForIn is much better suited for common use cases.

new AsyncIterator

Returns a new AsyncCondition object. It's methods are chainable. So you don't have to name the object.

AsyncIterator#generateWith fn

generateWith takes a function as the only parameter. The provided function is invoked every time we need decide whether to do another iteration or not. The provided function may return an array (which can be empty) which will be passed on to the callbacks of the forEach() method. If the provided function returns null then it is assumed that there can be no more iterations and the callback for the finally() method is invoked.

AsyncIterator#forEach fn

forEach takes a function as the only parameter fn. fn is called every time there is anything iterable. fn will receive a function next as the first parameter which must be called to signal the end of an operation. Any parameters returned by generateWith is sent to the fn.

AsyncIterator#next

next iterates to the next item.

AsyncIterator#stop

stop stops the iteration. similar to break in while loops.

AsyncIterator#finally fn

the finally() method takes a function as a parameter. The provided function (fn) is invoked only after iteration is complete. (i.e. generateWith returned null or stop was called)

Example: (In the example below, we actually iterate over an array and do some asynchronous operations)

testString = ''
 
array = [ 'A', 'B', 'C', 'D' ]
 
it = new AsyncIterator
 
it.generateWith (expectedIndex)-> 
  if expectedIndex < array.length then [ expectedIndex, array[expectedIndex] ] else null
 
it.forEach (next, index, item)->
  delay 10, ->
    testString += item + index
    next()
 
it.finally ->
  console.log testString # prints 'A0B1C2D3'
  done()

asyncWhile

asyncWhile let's you do asynchronous operations on a loop while a certain condition is true. (Just like the traditional while syntax)

Sync Code:

console.log "Before loop"
i = 0
while i < 10
  console.log "In loop"
  i++
console.log "After loop"

Same thing but asynchronously

console.log "Before loop"
i = 0
asyncWhile -> i < 10
.forEach (next)->
  console.log "In loop"
  i += 1
  next()
.finally ->
  console.log "After loop"

asyncForIn

asyncForIn let's you loop through each element of an array. (Just like the traditional for ... in ... syntax)

Sync Code:

array = [ 'A', 'B', 'C' ]
console.log "Before loop"
for item, index in array
  console.log "In loop. Item #{item}. Index #{index}"
console.log "After loop"

Same thing but asynchronously

array = [ 'A', 'B', 'C' ]
console.log "Before loop"
asyncForIn array
.forEach (next, item, index)->
  console.log "In loop. Item #{item}. Index #{index}"
  next()
.finally ->
  console.log "After loop"

asyncForOf

asyncForOf let's you loop through each key/value pair of an object/map. (Just like the traditional for ... of ... syntax)

Sync Code:

object = { a: 1, b: 2 , c: 3 }
console.log "Before loop"
for own key, value of array
  console.log "In loop. Key #{key}. Value #{value}"
console.log "After loop"

Same thing but asynchronously

object = { a: 1, b: 2 , c: 3 }
console.log "Before loop"
asyncForOf array
.forEach (next, key, value)->
  console.log "In loop. Key #{key}. Value #{value}"
  next()
.finally ->
  console.log "After loop"

AsyncCollector

AsyncCollector class is a rather innovative way to handle multiple asynchronous operation that converge to a single callback. It has a built in mechanism to collect arbitrary data.

new AsyncCollector numberOfParallelOperations

Returns a new AsyncCollector object. It's methods are chainable. So you don't have to name the object. It takes a single mandatory parameter. The number of parallel operations you intend to do.

AsyncCollector#collect [key, value]

collect marks 1 operation as done (i.e. increments numberOfOperationsDone). The number of operations left is computed by numberOfParallelOperations - numberOfOperationsDone. It optionally takes a key and a value which gets stored in an object which is passed to the fn of finally

AsyncIterator#finally fn

the finally() method takes a function as a parameter. The provided function (fn) is invoked only after iteration is complete. As a parameter, it will receive as a parameter the object in which the key/value pairs provided by collect method is collected.

Example:

col = new AsyncCollector 3
 
baselib.delay 10, -> 
  col.collect 'a', 1
 
baselib.delay 90, -> 
  col.collect 'b', 2
 
baselib.delay 180, -> 
  col.collect 'c', 3
 
col.finally (collection)->
  console.log collection # prints { a: 1, b: 2, c: 3}

Publisher

Publisher class is a lightweight alternative to NodeJS's EventEmitters. It has the option to publish data either sequentially or parallely allowing a much greater flow control. On sequencial operations, a subscriber can even choose to stop the propagation altogether, much like the Event#stopPropagation in DOM.

new Publisher

Returns a new Publisher object. It's methods are chainable. So you don't have to name the object.

Publisher#subscribe fn

the subscribe() method takes a function as a parameter. The provided function (fn) is put in a queue and called with the data provided by Publisher#publishInSeries or Publisher#publishInParallel.

For Publisher#publishInSeries, the fn's signature will be like fn = (next, stop, args...)->. where next is a function that must be called to signal the end of operation. stop can be called to stop the propagation of the data altogether (depriving the subsequent subscribers). These two parameters are followed by any data published.

For Publisher#publishInParallel, the fn's signature will be like fn = (next, args...)->. where next is a function that must be called to signal the end of operation. There is no stop operation as all the subscribers are notified in parallel. The next parameter is followed by any data published.

Publisher#unsubscribe fn

The unsubscribe() method takes a function as a parameter. If fn is already in queue it will remove it. Otherwise it has no effect

Publisher#publishInSeries data...

The publishInSeries method lets you publish your data to all the subscribers one by one. One subscriber must call the next function in order to propagate data to the next subscriber. The data you provided will be passed on to the subscribers.

Publisher#publishInParallel data...

The publishInParallel method lets you publish your data to all the subscribers parallely. The data you provided will be passed on to the subscribers.

AsyncIterator#finally fn

the finally() method takes a function as a parameter. The provided function (fn) is invoked only after either all the subscriber has called the next function or at least one has called the stop function.

Example:

testString = ''
 
pub = new Publisher
 
pub.subscribe (next, stop, arg1, arg2)->
  testString += 'A' + arg1 + arg2
  next()
 
pub.subscribe (next, stop, arg1, arg2)->
  testString += 'B' + arg1 + arg2
  stop()
 
pub.subscribe (next, stop, arg1, arg2)->
  testString += 'C'
  next()
 
pub.finally ->
  console.log testString # prints 'Aa1Ba1'
  done()
 
baselib.delay 50, ->
  pub.publishInSeries 'a', 1

shallowCopy

shallowCopy anyValue

shallowCopy copies all the properties/items in an object/array to a new one. If given a non-object / non-array value (i.e. Number, String etc), the value is returned.

It handles Regex and Date objects as well. Also, calls the constructor for user made classes and copies the properties. So, if there are no hidden properties, user made class instances should be copied reasonably reliably.

Example:

testObject = {
  a: 3
  b:
    ba: 4
    bb: 5
  c: [
    1
    2
    {
      r: 3
      x: 4
    }
  ]
  d: new Date
}
copiedObject = shallowCopy testObject
console.log testObject is copiedObject # false
console.log testObject.b is copiedObject.b # true

deepCopy

deepCopy anyValue

deepCopy copies all the properties/items in an object/array to a new one recursively. If given a non-object / non-array value (i.e. Number, String etc), the value is returned.

It handles Regex and Date objects as well. Also, calls the constructor for user made classes and copies the properties. So, if there are no hidden properties, user made class instances should be copied reasonably reliably.

Example:

testObject = {
  a: 3
  b:
    ba: 4
    bb: 5
  c: [
    1
    2
    {
      r: 3
      x: 4
    }
  ]
  d: new Date
}
copiedObject = shallowCopy testObject
console.log testObject is copiedObject # false
console.log testObject.b is copiedObject.b # false

once

once fn

once returns a function that can be called only once. Effectively you could say it converts a function so that it can be called only once. It passes on the execution context (a.k.a. this) reliably.

Example:

testInteger = 0

fn = -> testInteger += 1

oFn = once fn

oFn()
oFn()
oFn()

console.log testInteger # prints 1

merge

merge value1, value2

merge as the name suggests, merges two values (most usefully objects and arrays) into one. merge makes sure that the original values value1 and value2 are not altered in any way. However it does not ensure that the tree is fully unique. In order to get a guaranteed unique copy perform deepCopy merge value1, value2

Example:

    a = { a: 1, c: { d: 4, e: 5, k: [ 1, 4 ] } }
    b = { b: 2, c: { d: 4, f: 6, k: [ 2 ] } }
    m = merge a, b
   
    console.log m # prints { a: 1, b: 2, c: { d: 4, e: 5, f: 6, k: [ 1, 4, 2 ] } }

Package Sidebar

Install

npm i baselib

Weekly Downloads

0

Version

0.1.0

License

MIT

Last publish

Collaborators

  • ishafayet