All Projects → justsml → Escape From Callback Mountain

justsml / Escape From Callback Mountain

Licence: mit
Example Project & Guide for mastering Promises in Node/JavaScript. Feat. proposed 'Functional River' pattern

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Escape From Callback Mountain

Thunks
A small and magical composer for all JavaScript asynchronous.
Stars: ✭ 523 (+110.04%)
Mutual labels:  promise, callback
Bach
Compose your async functions with elegance.
Stars: ✭ 117 (-53.01%)
Mutual labels:  promise, callback
Zcoil
Elegant access to data
Stars: ✭ 20 (-91.97%)
Mutual labels:  promise, callback
react-patterns
react patterns examples
Stars: ✭ 39 (-84.34%)
Mutual labels:  patterns, hoc
Metasync
Asynchronous Programming Library for JavaScript & Node.js
Stars: ✭ 164 (-34.14%)
Mutual labels:  promise, callback
run exclusive
⚡🔒 Wait queue for function execution 🔒 ⚡
Stars: ✭ 22 (-91.16%)
Mutual labels:  promise, callback
Go Pattern Examples
Examples of implement for awesome go patterns including usual design patterns, in easy understanding examples.
Stars: ✭ 65 (-73.9%)
Mutual labels:  example, patterns
ProtoPromise
Robust and efficient library for management of asynchronous operations in C#/.Net.
Stars: ✭ 20 (-91.97%)
Mutual labels:  promise, callback
Poloniex Api Node
Poloniex API client for REST and WebSocket API
Stars: ✭ 138 (-44.58%)
Mutual labels:  promise, callback
React Redux Typescript Guide
The complete guide to static typing in "React & Redux" apps using TypeScript
Stars: ✭ 11,621 (+4567.07%)
Mutual labels:  guide, patterns
guide vue-cli-3-multiple-entry-points
Simple guide to show how to create multiple entry points (pages) using vue-cli-3
Stars: ✭ 29 (-88.35%)
Mutual labels:  guide, example
Nedb Promises
A dead-simple promise wrapper for nedb.
Stars: ✭ 190 (-23.69%)
Mutual labels:  promise, callback
dvc dask use case
A use case of a reproducible machine learning pipeline using Dask, DVC, and MLflow.
Stars: ✭ 22 (-91.16%)
Mutual labels:  guide, example
Replace In File
A simple utility to quickly replace contents in one or more files
Stars: ✭ 369 (+48.19%)
Mutual labels:  promise, callback
php-finder refactoring-kata
🐘🔍Incomprehensible Finder Refactoring Kata port for PHP
Stars: ✭ 22 (-91.16%)
Mutual labels:  refactoring, example
Gollback
Go asynchronous simple function utilities, for managing execution of closures and callbacks
Stars: ✭ 55 (-77.91%)
Mutual labels:  promise, callback
do
Simplest way to manage asynchronicity
Stars: ✭ 33 (-86.75%)
Mutual labels:  promise, callback
lightflow
A tiny Promise-inspired control flow library for browser and Node.js.
Stars: ✭ 29 (-88.35%)
Mutual labels:  promise, callback
Express Env Example
A sample express environment that is well architected for scale. Read about it here:
Stars: ✭ 130 (-47.79%)
Mutual labels:  example, guide
React Redux Typescript Realworld App
RealWorld App implementation based on "react-redux-typescript-guide"
Stars: ✭ 178 (-28.51%)
Mutual labels:  example, guide

Escape from Callback Mountain v2.6.0

Build Status

Refactoring JavaScript w/ Functional River Pattern

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 NodeJS/JavaScript.

What is the Functional River pattern?

It is an async & sync version of the Collection Pipeline pattern.

Your parameters/data represents the water, and functions form the riverbed.


Roughly speaking, my definition of pipeline is a sequential series of chained functions where arguments line up with return values (using Array methods, Promises, or similar). Key to my adaptaion is using named functions.

This ultimately results in your code reading like a step-by-step story.


Compare these 2 examples:

// ❌ Non-Functional River / Collection Pipeline Code ❌
const formatScores = scores => scores
  .map(x => x * 2.0)
  .map(x = x.toFixed(2))
// ✅ Functional River Code ✅
const formatScores = scores => scores
  .map(double)
  .map(formatNumber)

Let's look at a more complex example, with asynchronous requirements added in the mix...

Comparison: Callbacks vs. Functional River

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

The technique I demonstrate hopefully illustrates the Functional River pattern:

Functional River Goals/Benefits:

  • Higher level logic implemented with multiple smaller single-purpose functions, assembled to read like a story.
  • Decoupled modules are easier to maintain & upgrade over time.
  • Reduce bugs by relocating ad hoc logic. (e.g. one-off transformations, untested validation)
  • Use same interface for both synchronous & 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 - rebundle simple functions as needed to create new higher-order functions.
  • Increased testability - eliminate hidden surface area.
  • Substantially faster code readability - versus artisinal functions assembled with ad hoc code glue (a Big Ball of Mud).

Note: The Functional River Relies on ideas from Lisp to SmallTalk - adapted to a JavaScript world. Apologies to Promise Resistance Leader Brian Leroux. For alternative patterns please read my more detailed article demonstrating 4 JavaScript Composition Techniques (with Examples)

Have feedback, fixes or questions? Please create issues or Pull Requests. Or DM me at twitter @justsml.

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

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 & 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.)

Pattern Showdown

View Callback Comparison

Summary

It's perhaps true that an overly-done flat & modular JS Project can feel more disorganized over time. New practices & approaches must be explored (from monorepos, to breaking up modules when-needed to meet org/dev/deployment needs).

Project and code discipline is just as important as it's always been. Also, the community is still developing consensus around Functional JS patterns, immutability and overall project organization.

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 probably still too entangled (coupled).


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


Please Star this project ❤️


Wiki Contents

Wiki Main


Credits & Inspiration

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


v2.0 Released

Escape from Callback Mountain Key Updates

  1. README now more focused on the Functional River pattern.
    • Counter-examples are still included in ./src, just not featured on README.
  2. There's an updated production-ready library Functional Promises which grew out of the feedback & research from this Project.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].