From Promises to Futures in Javascript

Nadeesha Cabral
Jun 5, 2019 · 4 min read
Image for post
Image for post
Photo by Tomasz Frankowski on Unsplash

As someone who jumped on the Node.js train early on and wrestled with the callback hell, I quite liked promises. I still do, but more in an “it’s the best that we’ve got way”. So many times I’ve forgotten to a promise chain and it always decided to silently fail. Or, I'd have to hack my way through trying to cancel a promise chain.

I won’t go into too much detail over all of the issues with promises. But I highly recommend Broken Promises which does an excellent job of summarizing everything.

Fluture

As an alternative to promises, I’ve been using fluture for some time now. I highly recommend the fluture-js library for this. It introduces itself as a “Fantasy Land compliant (monadic) alternative to Promises”. If you’re unfamiliar with monads of fantasy-land specification, don’t worry about it.

Douglas Crockford once said, “Once someone understands Monads, they lose the ability to explain it to anybody else”. Despite that, let me give a shot at explaining what a monad is, in Javascript terms.

In Javascript world, a monad tends to be an object that’s got a bunch of functions as properties. When you invoke these functions, it tends to do something and return something like the original Javascript object. You can then use that returning object to do further computations. This might sound like a promise, but promises are not monads for reasons which we’ll not get into here.

Example

Let’s consider a simple futures example (using fluture-js) where we:

  1. Read and parses a package.json file to get the name of a package
  2. Send that data to a fictional API that returns some metadata about it
  3. Parse that result to get the downloads count
  4. Sends that count to an function

Explanation

I’d like to believe that you can elicit 80% of the value that fluture gives by knowing 20% of the constructs it provides. And this might well be that 20%. Let’s go through each construct of the earlier example in detail and see what it does.

0. Common to all constructs…

…is the fact that everything returns a future. Therefore, we can compose these futures, refactor them out, etc.

1. encaseP, node, encase creates futures

Since javascript doesn’t have futures, we need to have some tools to convert existing Javascript constructs like promises, and functions to futures. And these 3 constructs does exactly that.

  1. encaseP creates a future from a promise
  2. node creates a future from a node-style async function
  3. encase creates a future from a plain old javascript function

Since all three of these Javascript constructs may fail through a promise rejection, a callback(error) or an exception, these three utilities map that rejection state to future rejection as well.

2. pipe lets you chain futures

Think of this like the that you get when you invert the function. We talked about this in length at A practical guide to writing more functional JavaScript. It basically lets you "chain" futures.

3. map transforms values

When you have to pipe something, you always have to pipe a -ed version of your result. When your computation doesn't produce a future, like , you can do that transformation inside of .

When you invoke , with a future, takes the value inside that future, applies to transform that value, and returns the value wrapped in a future.

4. chain transforms values but expects a future back

does the same thing map doesn't, but the in your must return a future back.

5. fork to execute

Since futures are lazily evaluated, nothing is being done until you tell the future to execute things. takes a success function and a failure function and executes them on a success/failure instance.

Why use futures over promises?

There’s a lot of advantages to using futures. Aesthetically pleasing API is a big part of it. Since promises are the real competition, let me try to make a few concrete distinctions against promises.

  1. Lazy evaluation has a lot of practical advantages. You have a guarantee that your computation will not execute at the time of creating the future. Whereas the will execute at the time of the creation.
  2. Testability comes through lazy evaluation as well. Instead of mocking all of your side-effects when testing, you can assert whether the futures wrapping the side effects were “composed”. Thereby not executing the side-effects as well.
  3. Better control flow than promises for sure. You can , -ize, and cancel one or more futures out of the box.
  4. Error handling is far more superior. You will always end up with an error. No more forgotten es silently suppressing errors. And you get a really good debugging experience out of the box.

Real life example

As a more concrete example, I’ve written a CLI application for parsing and searching JSON data with futures. The application itself is stateless and can parse and search JSON files much bigger than the system’s memory limit.


Originally published at https://write.as on June 5, 2019.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store