Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Escape From Callback Mountain!

DZone's Guide to

Escape From Callback Mountain!

In this article, we explore functional promise patterns in JavaScript and walk through the refactor of callback-based Node.js/JavaScript code.

· Web Dev Zone
Free Resource

Never build auth again! Okta makes it simple to implement authentication, authorization, MFA and more in minutes. Try the free developer API today! 

Refactoring NodeJS/JavaScript: A 2017 Guide

I am a big fan of Functional Programming and Modular JavaScript. This project's goal is to demonstrate the latest Functional Promise patterns while taking you through a refactor of real world callback-based Node.js/JavaScript.

The overall technique I demonstrate is what I call the 'Functional River' pattern, where your input/parameters/data is the water, and the code forms the riverbed. More or less, it is an async+sync version of a Collection Pipeline pattern.

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 and jargon.

'Functional River' Goals/Benefits:

  • Higher level logic implemented with multiple smaller single-purpose functions assembled to read like a story.
  • Reduce bugs by eliminating ad hoc logic (e.g. one-off transformations, untested validation).
  • Use the same interface for both synchronous and asynchronous code ( promise.then(value => alert(value))).
  • Prefer immutable, stateless code as essential building blocks.
  • Less elaborate, modular code is naturally more reusable.
  • Easier to move logic around - re-bundle simple functions as needed to create new higher-order functions.
  • Increased testability - eliminate hidden logic.
  • Substantially faster code readability - versus methods which muddle the important parts, and further hides ad hoc error/glue code.

Note: Relies on ideas from Lisp to SmallTalk - adapted to a JavaScript world. Additionally, I happen to use Bluebird Promises. Apologies to Promise Resistance Leader Brian Leroux. For alternative patterns please read my more detailed article demonstrating 4 Functional JavaScript Techniques (with Examples)

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

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

Example Task:

Authentication Method

Here's a rough visualization of our function: image

Before

Node-Style Callbacks With 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  (read the code: PR #2: Flatten Functions)
  2. Step 2: DRYer Code (read the code: PR #3: DRYer Code)
  3. Step 3: Cleanup Code (read the code: PR #5: Post Cleanup)

Pros and Cons

Pros

  • Reducing ad hoc code (arbitrary input checks, UI hacks, etc.) results in:
    • More uniform code between different teams and developers.
    • Performance tooling and refactoring is an appreciably better experience.
    • More certainty about code correctness.
    • Higher code reuse.
  • 100% Unit Test Coverage is Possible
    • Unit tests uniquely prove you found, understand, AND resolved a given bug.
    • Faster bug resolution process.
  • Flatter code hierarchy == less filler to remember
    • Re-organizing code becomes easier and less prone to bugs with tests and Pure-ish Functions.

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 and 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 comfort 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- and Stream-based code are not improved much, if at all, using this technique. Look into RxJS.
    • Ongoing experiments include simple closures, extending Promise with EventEmitter, or using Bluebird's .bind to inject variable state into the Promise chain (I know, "ugh side-effects, gross." PRs welcome).

This area of Functional JS patterns and consensus around its best practices has plenty of room to go.

Concerns

Some really smart people out there have pointed out potential problems with over-modularization.

image

While true of most coding patterns, an overly-done flat and modular JS Project can feel 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 and rearrange modules with low risk. If this still feels risky, your modules are still too entangled.

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

Credits and Inspiration

I highly recommend reading (or watching) every single link here.

Launch your application faster with Okta’s user management API. Register today for the free forever developer edition!

Topics:
javascript ,functional programming ,web dev ,function composition

Published at DZone with permission of Daniel Levy. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}