New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

laissez-faire

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

laissez-faire

A promise implementation. Simple and fast

  • 0.7.0
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

Laissez-faire

Its a promise class.

It works in node and the browser. Anything in the name? Yea its a French word meaning "let it be". Seemed apt for a library designed to eliminate the need to worry about when computation occurs.

Differences from other libraries

Laissez-faire is compliant with the Promises/A spec. However, it does break from some patterns which are common in other implementations and found in the Promise/A+ spec:

  1. Doesn't guarantee async calls to then; you use promises to abstract away the effects of time. Therefore, there shouldn't be any need to guarantee anything about when promises will be fulfilled. The reasoning behind this feature is usually that it makes promises easier to understand for beginners. I think its more like this feature makes it easier for users to misunderstand promises and treat them like a less ugly version of the callback pattern found in Node.

  2. Paranoia is optional; a goblin proof proxy object is not created when you create a new instance of laissez. If you want to be able to pass out access to your promises value without allowing them to also assign a value to your promise you call promise.proxy() A new proxy is generated with each call so you can pass out as many as you like. These are robust proxies unlike some implementations which don't freeze there proxy objects and therefore are susceptible to being decorated by a goblin. A decorate-able proxy is just as insecure as no proxy at all (implementations which don't freeze their proxies do so for performance).

  3. API:

  4. new Laissez instead of lib.defer()

  5. deferred.proxy() instead of deferred.promise

The test suit is comprised mainly of the promises test suite written by Domenic Denicola. I leave out the always-async test since I disagree with the concept. I have added a few extra suites for Laissez-faire specific features.

Performance

Good it seems.

====================================================
Test: promise-sequence (10,000 iterations)
----------------------------------------------------
Name              Time ms       ns/run       Diff %
laissez                14         1436            -
when                   18         1819           27
deferred               25         2524           76
avow                  103        10278          616
rsvp                  235        23455         1533
jquery                382        38244         2563
q                     718        71794         4900

====================================================
Test: promise-create (10,000 iterations)
----------------------------------------------------
Name              Time ms       ns/run       Diff %
laissez                 0           47            -
avow                    3          329          601
when                   30         3006         6303
deferred               30         3026         6347
rsvp                   35         3503         7362
q                     104        10393        22042
jquery                265        26545        56455

What makes it better

  1. Smaller
  2. Faster
  3. Easier to debug (uncaught errors are logged)
  4. Flexible API

What makes it worse

  1. I haven't got around to doing much interope testing so will most likely have some bugs if you are passing it promises from other libraries.

Basic Usage

var promise = new Promise();

promise.then(function(value) {
  // success
}, function(value) {
  // failure
});

// later...

promise.resolve(value) // triggers first callback
// or...
promise.reject(error) // triggers second callback

Once a promise has been resolved or rejected, it cannot be resolved or rejected again.

Here is an example of a simple XHR2 wrapper written using Laissez-faire:

var getJSON = function(url) {
  var promise = new Promise()

  var client = new XMLHttpRequest()
  client.open("GET", url)
  client.onreadystatechange = handler
  client.responseType = "json"
  client.setRequestHeader("Accept", "application/json")
  client.send()

  function handler() {
    if (this.readyState === this.DONE) {
      if (this.status === 200) promise.resolve(this.response)
      else promise.reject(this)
    }
  };

  return promise;
  // or if you you think there might be goblins in your system
  // return promise.proxy()
};

getJSON("/posts.json").then(function(json) {
  // continue
}, function(error) {
  // handle errors
});

Understanding promises

I struggled to understand promises and that is why I started playing with this project. It would be rude of me not to share my learnings. I hope my explanation prevents you from needing to take the same route in order to understand them. Every explanation I have read begins like this: "A promise represents the eventual value returned from the single completion of an operation". I have read a few and they all sounded suspiciously wrote. That one was from the Promises/A spec. Its a shame they all choose to use the word "eventual" since it is actually slightly misleading. Promises may be resolved now. Therefore, a simpler and more accurate way of introducing promises would be to say "A promise represents the value of a computation". This of course would raise the question: isn't that what variables are for? Yes. Except variables break if the computation occurs at any time other than right now. The problem promises are designed to solve is uncertainty due to the timing of processes. Promises take the worry away. They add some messy syntax but allow you to focus on the process which will ultimately makes things easier to reason about. In order to do this they need to do two things.

  1. Store the result of the computation as soon as it happens
  2. Provide a mechanism to connect further computations which depend on this value

By providing these capabilities they allow you to ignore the timing of computation completely. They could easily become a language feature complimenting the less powerful variable.
You saw promises in use in the previous section but lets walk through how they are used since they look nothing like variables syntactically. First we create the promise: var promise = new Promise. So we need to use a variable in order to use a promise. You could say then that we are creating an enhanced variable or more simply a container. Then we do some computation and assign our value to the promise return promise.assign('some value'). Note you must always return the promise now though you don't have to assign to it until you are good and ready. So your functions must know how to manage a promise, its a shame since semantically they aren't much different from variables, though blame that one on the language designers. Maybe one day JavaScript will be inspired and adopt message passing like Io and Smalltalk, maybe.

In summary promises are a way of storing and accessing values devoid of when. That makes them very useful for situations where you can't be certain something is going to be sitting in memory ready to go but would like to take advantage of the speed if it is. Callbacks enable this and will always be faster, however promises provide the value and error propagation infrastructure to make these programs easier to reason about. Plus implementations like Laissez-faire are actually fairly light weight so offer good bang for your buck. The time they save you coding can be spent sleeping at night.

What promises are not

After writing this I realized promises could easily be mistaken for lazy evaluation. Lazy evaluation is a strategy to delay computation until results are actually requested. This is a good strategy to use when operating over collections since often you end up only using a small amount of the collection to get your result. It prevents you from concerning yourself with efficiency and allows you to program more declaratively. Meanwhile promises don't actively delay computation (at least they shouldn't) they simply provision for the scenario that its is delayed (due to io or network latency). Using promises still results in a proactive computation strategy. Meaning functions are run as soon as possible, but no sooner. In contrasting that statement with lazy evaluation you could say that lazy evaluation delays computation as long as possible, but no longer.

The benefit of promises

They allow you to compose the results of external computation with those of internal. By external I mean that which occurs anywhere from another process to another computer on the other side of the Internet. By internal computation I mean anything that can be done immediately. In JavaScript we consume a lot of external computations, therefore, promises should be and will become staple. They are the only tool I am aware off that properly abstracts the problems caused by distributed computation.

Chaining

If you return a regular value, it will be passed, as is, to the next handler.

getJSON("/posts.json")
.then(function(json) {
  return json.post
})
.then(function(post) {
  // proceed
});

Returned promises are recognized as promises and have their resolved value used to resolve the promise they were returned to

getJSON("/post/1.json")
.then(function(post) {
  // save off post
  return getJSON(post.commentURL)
})
.then(function(comments) {
  // proceed with access to posts and comments
})

Error Handling

Errors also propagate:

getJSON("/badurl.json")
.then(function(posts) {

})
.then(null, function(error) {
  // even though no error callback was passed to the
  // first `.then`, the error propagates
})

Technically a promise can never throw an error since at any time in the future a user could add a child promise and handle the exception. However in practice the promise user will bind their error handlers immediately, so in most cases unhandled rejections which sit around for more than one tick of the event loop are genuine errors. Laissez-faire provides a mechanism for you to hook into these errors. See the source for details. It actually uses this mechanism itself to provide a nice default behavior which is to log the errors to the console after a 1 second delay. If this doesn't suit you then it is easy to override the behavior.

Finishing with a promise

If you have a promise and you only need to access it value; that is to say you won't be returning a value from the next operation, then you have no need for the promise normally generated by the call to then. This is the situation where a callback as commonly seen in node.js would be sufficient for you needs. In fact this is a very common scenario. It must be since a lot of people get by with callbacks as their only tool dealing with async operations. Laissez-faire provides a method for this scenario. end. Using this method creates a new processing branch; as with then, except you are saying this is the last thing I am doing over here. Laissez-faire knows it is safe to throw errors then since you can't of intended for them. Furthermore, since you don't need a new promise end returns the old one. This is a nice API feature as you will see in the following example since it allows you to form more fluent chains.

new Promise().assign(1)
.end(function(val){
  // val === 1
  return val + 1
})
.end(function(val){
  // val === 1
  return val + 1
})
.end(function(val){
  throw new Error
})
.then(function(val){
  // val === 1
  return val + 1
}, function(){
  // will not handle exception since it is on another branch
})
.then(function(val){
  // finally val === 2
  console.log(val)
  return val + 1
})

I'd be surprised if that code sample conjured the picture in your head I wanted so hear is a diagram illustrating the structure of the process.

 process

Each box represents a process of some kind. The circles below some of the processes represent the values they expose. The first box creates the promise and exposes itself for binding. The three calls to end each create a process but don't create new promises. Therefore, any values they might generate are lost. The fourth call, to then, does the same thing as the others except it exposes a value to the outside world. This value is represented by a promise, therefore to access it we must use either then or end. In this case we used then which resulting in the final value being exposed. This final value is 2. The flow of data is represented by the arrows with the red boxes showing the value of the data. Hopefully this illustration makes it obvious what is happing when you access the value of a promise via then or end, you are creating a branch in the process. Values flow down these branches in the same way as errors. Though they are of course handled by separate functions. Because we chose to use then as the final call on the fourth branch we will be able to continue processing at any time where we left of. Be that with an error or a value, no matter, so long as the p variable doesn't go out of scope.
In summary then we can say promises provide a mechanism to freeze process. Ready to defrost and continue via then and end as soon or as long as you like.

Mutability

Its widely considered that promises should be immutable and I agree. I though its interesting to note what happens to promises when they are implemented in a mutable way. So when a promise has its value changed it propagates this value to any child promises. At which point you would of switched to the Reactive programming paradigm. The change in implementation to create this effect is trivial. I guess we can say then that promises borderline bring a new paradigm to JavaScript. Its amazing how flexible this language is.

API

Promise()

Promise class

var promise = new Promise

exports.prepareException(promise:Promise, The:Error e)

Dafualt uncought error handler. It will wait 1 second for you to handle the error before logging it to the console. Feel free to replace this function with one you find more useful. Its purely a debugging feature. Technically promises throw unhandled exceptions. They just sit around waiting for you to handle them

exports.cancelException(promise:Promise)

Sometimes error handlers may be added after a promise has been rejected. If you would like to cancel any undhandled exception handling logging logic you can do so by replaceing this function

exports.rejected

Helper to quickly create a rejected promise

exports.fulfilled

Helper to quickly create a completed promise

proto.then(done:Function, fail:Function)

Create a follow up process

  promise.then(
      function(value){
          return value
      },
      function(error){
          return correction
      }
  )

proto.end(done:Function, fail:Function)

Like then but is designed for terminal operations. So any values generated will not be available for follow up and uncaught errors will be thrown. Use this method in place of then when you don't plan to do anything with the stuff generated by your callbacks

proto.stub

@alias end

proto.fail(fn:Function)

Create a terminal follow up branch with no success callback

proto.valueOf(done:Function, fail:Function)

If you pass callbacks it will act like .end otherwise it will attempt to return its resolution synchronously

proto.otherwise(errback:Function)

Convenience function for creating a propagating error handler

proto.always(callback:Function)

Convenience function to bind the same function to both done and fail

proto.finish(callback:Function)

A terminal version version of always

proto.node(callback:Function)

Allows the use of node style callbacks

proto.nend(callback:Function)

Provides simple access to the promises value via a node style callback

proto.resolve(value:!Error)

Give the promise it's value and trigger all process branches

proto.assign(unknown:Any)

Delegates to resolve or reject, depending on the value given

proto.reject(e:Error)

Break the promise and propagate the error to subsequent process branches

proto.yeild(value:Any)

Create a promise destined to resolve with a given value

  function () {
      var result = 3
      return promise.then(function(value){
          // something side effect
      }).yeild(result)
  }

proto.proxy()

Generate a safe interface for a promise

Keywords

FAQs

Package last updated on 14 Dec 2012

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc