Functional Promises in JavaScript Crash Course

Updated

Reading time ~8 minutes

Functional Promises in JavaScript Crash Course

See the companion project to this article: escape-from-callback-mountain Build Status

Refactoring NodeJS/JavaScript - a 2017 Guide

I am a big fan of Functional Programming and Modular JavaScript. This project’s goal is to demonstrate better coding habits by guiding you through a refactor of some real-world NodeJS JavaScript code.

The overall technique I demonstrate is what I call the ‘Functional River’ pattern. Where your input/parameter/data is the water, and the code forms the riverbed. More or less.

To the Haskell pros out there, before you flame me for not defining ‘monad’, this is meant to be a more welcoming place. So forgive me if I skip the overblown theory & jargon.

For this project I happen to use Bluebird Promises. Apologies to Promise Resistance Leader Brian Leroux.

‘Functional River’ Goals/Benefits:

Note: Relies on ideas from Lisp to SmallTalk - adapted to a JavaScript world.

Have feedback, fixes or questions? Please create issues or PRs. Or just complain at me on https://twitter.com/justsml

If you feel this subject has been thoroughly explored, please see my post Beating a dead horse?


Sample Task:

Authentication Method

Here’s a rough visualization of our function: image

See both Before and After examples below.

Before

Node-style Callbacks w/ Nesting

Note: This is intentionally reasonable callback code. Even if nested. Not trying a straw-man attack.

callback-mountain-before

After

‘Functional River’ Pattern

callback-mountain-after

Key Steps

  1. Step 1: Break Up The Big Functions - see the PR #2: Flatten Functions
  2. Step 2: DRYer Code - see the PR #3: DRYer Code
  3. Step 3: Cleanup Code - see the PR #5: Post Cleanup

Pros & Cons

Pros

  • Less ad hoc code results in:
    • More uniform code between different teams & developers,
    • Performance tooling & refactoring is an appreciably better experience,
    • More certainty about code correctness,
    • Higher code reuse.
  • 100% Unit Testability
    • Unit tests uniquely prove you found, understand, AND resolved a given bug,
    • Faster bug resolution process,
  • Flatter code hierarchy == less filler to remember

Cons

  • Performance. I’ve run some micro-benchmarks - it’s not awesome. However, 3 important things:
    1. It’s not meaningfully slower in real world applications.
    2. If it is necessary, performance analysis & tuning is a much improved experience. Smaller functions make it easier to see where slow code lurks - especially if you profile unit tests.
    3. As more people adopt these patterns, things will improve. V8/Chrome has been impressively fast at optimizing for emerging patterns.
  • Debugging can be more difficult. Though I have updated my dev tricks to debug this style of code, even without the confort of Bluebird’s error handling. I’ll add more sample scripts for this later.
  • Something new to learn. Deal with it, you’re a developer.
  • If you have an existing project with lots of code, the unfortunate reality is: Refactors Suck.
  • EventEmitter- & Stream-based code is not improved much, if at all, using this technique. Look into RxJS
    • Ongoing experiments include simple closures, extend Promise with EventEmitter, or using Bluebird’s .bind to inject variable state into the Promise chain. (I know, “ugh side-effects, gross.” PRs welcome.)

Any consensus around best practices is still far off, Functional JS patterns still vary.

Concerns

Some really smart people out there have reservations about over-modularization.

image

While true of other coding patterns, an overly-done flat & modular JS Project can get more disorganized over time. Project and code discipline is just as important as it’s always been. Also, we’re still developing consensus around Functional JS patterns.

Another solution I’ve found is to add a Code Style Guide preferably with naming conventions - see my thoughts on that subject. This becomes much more important as team size grows.

When done right, one of Functional River’s greatest strengths is the ability to relocate & rearrange modules with low risk. If this still feels risky, your modules are still too entangled.


Ultimately my goal is to better understand & advance Modular + Functional JS patterns. Hopefully I can sway some of the skeptics along the way :crossed_fingers:


Please read my more detailed article demonstrating 4 Functional JavaScript Techniques (with Examples)

Credits & Inspiration

Linux Server Benchmarking Scripts

Goal is a simple set of bash scripts which allow rapid assessment of any given hardware.Currently only CPU & HDD tests are wired up...… Continue reading