laissez-faire

0.12.4 • Public • Published

Laissez-faire

A simple promise class

Getting Started

If your new to the idea of using promises in JS or just this particular implementation of then I recommend you start by downloading this repo and having a quick run through the koans. To do that run.

$ git clone https://github.com/jkroso/Laissez-faire.git && cd Laissez-faire
$ npm install
$ make koans

installing

With component

$ component install jkroso/Laissez-faire

With npm

$ npm install jkroso/Laissez-faire --save

then in your app:

var Promise = require('Laissez-faire')

see the API documentation for usage details

About promises

What's a javascript promise

A promise is an Object which boxes a value and provides an interface for accessing it (proxies). They, therefore, are similar in essence to javascript's variables though they don't benefit from any syntactic abstraction. The thing promises have over variables though is that they can provide access to values which don't yet exist in the machines local memory. Accessing a promise's value is always done by sending it a function. This way if the promise doesn't have direct access to its value it can simply store the function and call it when the value arrives. Of course if the promise already has access to its value then there is no need to do anything fancy at all and it can just call the function immediately. The point is the interface is consistent regardless of how long it takes for the value to arrive so we can get on with building abstractions.

Composition

If you read the paragraph above you may be left questioning the value of promises since everything I described can be done with simpler methods like continuation passing (CPS). If you implement an API using CPS you are saying I might not be able to deliver my result immediately so pass me a function and I promise to deliver my result to it as soon as I can. The problem with this approach though is that their is nothing left sitting around. The concept of a promise exists but only ephemerally inside the context of the callback. Promise's provide a way of concretising the concept and pulling it out of the hidden context of the callback. By pulling them out its much easier to compose them with other functions. Why hide work from yourself? Lets explore this problem by sequencing two operations which increment a number.

When done with synchronous functions capable of directly returning their result it looks like this:

function inc(a){
    return a + 1
}
inc(inc(a))

Now lets pretend a isn't available right now and try deal with it using CPS.

function getA(cb){
  // normally a delay here
  cb(1)
}
getA(inc)

Its easy to do the first increment but how do we do the second. To do this we need access to the result of inc but we can't return the result from inc because its called by getA at some undefined time in the future. I have a few ideas:

  • make inc take a callback
    Nope. how would you then wire the first inc to call the second. This would rely on getA forwarding the second callback on to inc. That gets messy fast and I've never seen a callback taking function implemented to do that.
  • extend inc
    Nope this breaks the black box abstraction that we want inc to be.
  • create a wrapper function
    This is lame for similar reasons to the previous suggestion. We want to write logic not mechanics

Lets skip the CPS options though and see whats possible when you use promise objects instead of the limited scope promises callbacks provide.

First lets learn the API of promises which is just two methods. read and write. read takes a function which it calls with the promises value as soon as it can. While write is how you give a promise its value. Thats it. Now lets re-write getA to return a promise object instead of taking a callback.

function getA(){
  var p = new Promise
  // normally we aren't able to .write() immediately
  p.write(1)
  return p
}
getA().read(function(a){
    // `a` available here
})

This is all the knowledge of promise objects we need to implement our double inc program declaratively.

var a = getA()
var b = new Promise
var c = new Promise
a.read(function(a){
    b.write(inc(a))
})
b.read(function(b){
    c.write(inc(b))
})

Admittedly on the surface this looks like procedural code but thats because its mostly boilerplate which itself is in-fact procedural. What this code says is a is some value. b is the inc of that and c is the inc of that. Nothing here manages the ordering and in fact it could be rearranged without affecting the result. Lets see if we can make the code read a bit more like it behaves though. Notice the pattern for creating a promise for the transformed value of another promise is:

  • instantiate a promise object
  • read from the parent promise
  • write the transformed value to the new promise

We can use a function to abstract away this pattern.

function when(promise, transform){
  var p = new Promise
  promise.read(function(value){
    p.write(transform(value))
  })
  return p
}

with when the task of double incrementing a becomes:

var a = getA()
var b = when(a, inc)
var c = when(c, inc)

This is nice and declarative though in this example we don't really care about storing the intermediate values a, b, and c. I just wrote it that way to keep the flow readable. It could just as readily be written as:

when(when(getA(), inc), inc)

Which highlights a general readability issue when composing functions. Notice how to follow the flow of data in this last example you have to read the code backwards (right to left). Ideally the order our functions appear in our code and the order they execute would be the same. e.g:

getA().then(inc).then(inc)

And in JS that can be achieved by basically just plunking when on to the promise object as a method. The only change you have to make to when is to swap the promise argument for this. Its a shame really that we have to make such a superficial change in order to convert a function to a method but regardless its nice JS gives us the option to create methods.

So thats it I've introduced you to promises as objects and walked you through a very common abstraction used when working with them. Hopefully, you appreciate that promise objects aren't really abstractions themselves but instead the concretion of the concept of a promise which would otherwise be realised ephemerally and scoped within a single callback (if your were to use CPS). I like to think that by using promise objects in my code I am keeping all my cards on the table face up. With explicit promises adding/removing features from my programs is usually just a matter of adding and removing functions.

Further reading

There is a lot more to promises but I have chosen to stop here for fear of loosing the forest for the trees. Notably I have not talked about the story promises provide for handling errors.

links:

Just remember that at its heart a promise is just a more robust variable, thats were their value comes from. Its hard to see them for what they are when in JS at least they always come bundled with control flow abstractions.

Running the tests

$ npm install
$ make

Then open your browser to the ./test directory.

Note: these commands don't work on windows.

License

MIT

Readme

Keywords

none

Package Sidebar

Install

npm i laissez-faire

Weekly Downloads

2

Version

0.12.4

License

MIT

Last publish

Collaborators

  • jkroso