{"id":13493018,"url":"https://github.com/justsml/escape-from-callback-mountain","last_synced_at":"2025-08-23T13:19:47.052Z","repository":{"id":38898538,"uuid":"90344706","full_name":"justsml/escape-from-callback-mountain","owner":"justsml","description":"Example Project \u0026 Guide for mastering Promises in Node/JavaScript. Feat. proposed 'Functional River' pattern","archived":false,"fork":false,"pushed_at":"2023-02-24T23:44:51.000Z","size":70351,"stargazers_count":258,"open_issues_count":12,"forks_count":25,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-05-20T08:03:36.870Z","etag":null,"topics":["callback","callback-mountain","callbacks","example","functional-river","guide","hoc","javascript","modular","modular-js","nodejs","patterns","promise","refactoring"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/justsml.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2017-05-05T06:34:25.000Z","updated_at":"2024-07-09T09:16:23.000Z","dependencies_parsed_at":"2023-10-20T22:01:13.678Z","dependency_job_id":null,"html_url":"https://github.com/justsml/escape-from-callback-mountain","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/justsml/escape-from-callback-mountain","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justsml%2Fescape-from-callback-mountain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justsml%2Fescape-from-callback-mountain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justsml%2Fescape-from-callback-mountain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justsml%2Fescape-from-callback-mountain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/justsml","download_url":"https://codeload.github.com/justsml/escape-from-callback-mountain/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justsml%2Fescape-from-callback-mountain/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271749047,"owners_count":24814114,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-23T02:00:09.327Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["callback","callback-mountain","callbacks","example","functional-river","guide","hoc","javascript","modular","modular-js","nodejs","patterns","promise","refactoring"],"created_at":"2024-07-31T19:01:11.385Z","updated_at":"2025-08-23T13:19:47.013Z","avatar_url":"https://github.com/justsml.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Resources"],"sub_categories":["Examples and Exercises"],"readme":"# Escape from Callback Mountain v2.6.0\n\n[![Build Status](https://travis-ci.org/justsml/escape-from-callback-mountain.svg?branch=master)](https://travis-ci.org/justsml/escape-from-callback-mountain)\n\n## Refactoring JavaScript w/ Functional River Pattern\n\nI 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.\n\n### What is the `Functional River` pattern?\n\nIt is an async \u0026 sync version of the [Collection Pipeline](https://martinfowler.com/articles/collection-pipeline/) pattern.\n\nYour **parameters/data** represents the _water_, and **functions** form the _riverbed_.\n\n-----------------------------\n\nRoughly 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. \n\nThis ultimately results in your code reading like a step-by-step story.\n\n-----------------------------\n\nCompare these 2 examples:\n\n```js\n// ❌ Non-Functional River / Collection Pipeline Code ❌\nconst formatScores = scores =\u003e scores\n  .map(x =\u003e x * 2.0)\n  .map(x = x.toFixed(2))\n```\n\n```js\n\n// ✅ Functional River Code ✅\nconst formatScores = scores =\u003e scores\n  .map(double)\n  .map(formatNumber)\n```\n\n\nLet's look at a more complex example, with asynchronous requirements added in the mix...\n\n\n### Comparison: Callbacks vs. Functional River\n\n\u003e See both [Before](#before) and [After](#after) examples below.\n\n## Before\n#### Node-style Callbacks w/ Nesting\n\n\u003e **Note:** This is intentionally _reasonable_ callback code. Even if nested. Not trying a [straw-man](https://en.wikipedia.org/wiki/Straw_man) attack.\n\n![callback-mountain-before](https://cloud.githubusercontent.com/assets/397632/25775652/5e49b444-3267-11e7-937c-8b786da9314a.png)\n\n## After\n#### 'Functional River' Pattern\n![callback-mountain-after](https://cloud.githubusercontent.com/assets/397632/25775651/5e499aae-3267-11e7-8f08-2150730189b4.png)\n\n\u003c!-- \n\n#### Functional River Highlights\n![Functional River Highlights](https://user-images.githubusercontent.com/397632/38474143-e96bf632-3b57-11e8-8589-cbe3b3782d1a.gif)\n\n--\u003e\n\nThe [technique I demonstrate](#after) hopefully illustrates the **_Functional River_ pattern:** \n\n### _Functional River_ Goals/Benefits:\n\n* Higher level logic implemented with **multiple smaller single-purpose functions, assembled to read like a story.**\n* Decoupled modules are easier to maintain \u0026 upgrade over time.\n* Reduce bugs by relocating ad hoc logic. (e.g. one-off transformations, untested validation)\n* Use same interface for both synchronous \u0026 asynchronous code. (`promise.then(value =\u003e alert(value))`)\n* Prefer immutable, stateless code as essential building blocks.\n* Less elaborate, modular code is naturally more reusable.\n* Easier to move logic around - rebundle simple functions as needed to create new higher-order functions.\n* Increased testability - eliminate hidden surface area.\n* Substantially faster code readability - versus artisinal functions assembled with ad hoc code glue ([a Big Ball of Mud](https://en.wikipedia.org/wiki/Big_ball_of_mud)).\n\n\u003e Note: The Functional River Relies on ideas from Lisp to SmallTalk - adapted to a JavaScript world.\n\u003e Apologies to `Promise Resistance Leader` [Brian Leroux](https://twitter.com/brianleroux). For alternative patterns please read my more detailed article demonstrating [4 JavaScript Composition Techniques (with Examples)](http://www.danlevy.net/2017/03/10/functional-javascript-composition/)\n\n#### Have feedback, fixes or questions? Please create [issues](https://github.com/justsml/escape-from-callback-mountain/issues/new) or [Pull Requests](https://github.com/justsml/escape-from-callback-mountain/compare). Or DM me at [twitter @justsml](https://twitter.com/justsml).\n\nIf you feel this subject has already been exhauted, please see my post [Beating a dead horse?](https://github.com/justsml/escape-from-callback-mountain/wiki/Beating-a-dead-horse%3F)\n\n\n## Key Steps\n\n1. [Step 1: Break Up The Big Functions](https://github.com/justsml/escape-from-callback-mountain/wiki/Step-1:-Break-Up-The-Big-Functions) - read the code: [PR #2: Flatten Functions](https://github.com/justsml/escape-from-callback-mountain/pull/2/files?diff=unified)\n1. [Step 2: DRYer Code](https://github.com/justsml/escape-from-callback-mountain/wiki/Step-2:-DRYer-Code) - read the code: [PR #3: DRYer Code](https://github.com/justsml/escape-from-callback-mountain/pull/3/files?diff=unified)\n1. [Step 3: Cleanup Code](https://github.com/justsml/escape-from-callback-mountain/wiki/Step-3:-Post-Cleanup) - read the code: [PR #5: Post Cleanup](https://github.com/justsml/escape-from-callback-mountain/pull/5/files?diff=unified)\n\n\n## Pros \u0026 Cons\n\n#### Pros\n\n* Less ad hoc code results in:\n  * More uniform code between different teams \u0026 developers,\n  * Performance tooling \u0026 refactoring is an appreciably better experience,\n  * More certainty about code correctness,\n  * Higher code reuse.\n* 100% Unit Testability\n  * Unit tests uniquely prove you found, understand, AND resolved a given bug,\n  * Faster bug resolution process,\n* Flatter code hierarchy == less filler to remember\n  * Re-organizing code is easier \u0026 less prone to bugs - with [Pure-ish Functions](https://en.wikipedia.org/wiki/Pure_function)\n\n\n#### Cons\n\n* Performance. I've run some micro-benchmarks - it's not awesome. However, 3 important things:\n  1. It's **not meaningfully slower in real world applications.**\n  1. If it is necessary, performance analysis \u0026 tuning is a much improved experience. Smaller functions make it easier to see where slow code lurks - especially if you profile unit tests.\n  1. As more people adopt these patterns, things will improve. V8/Chrome has been impressively fast at optimizing for emerging patterns.\n* 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.\n* Something **new to learn**. Deal with it, you're a developer.\n* If you have an existing project with lots of code, the unfortunate **reality is: Refactors Suck**.\n* EventEmitter- \u0026 Stream-based code is not improved much, if at all, using this technique. Look into RxJS\n  - 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.)\n\n\n## Pattern Showdown\n\n#### [View Callback Comparison](docs/comparison-callbacks.md)\n\n\n\n## Summary\n\nIt's perhaps true that an overly-done flat \u0026 modular JS Project can feel more disorganized over time. New practices \u0026 approaches must be explored (from monorepos, to breaking up modules when-needed to meet org/dev/deployment needs).\n\nProject 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.\n\nWhen done right, one of _Functional River's_ **greatest strengths** is the ability to **relocate \u0026 rearrange** modules with **low risk**. If this still feels risky, your modules are probably still too entangled (coupled).\n\n----------\n\n#### Ultimately my goal is to better understand \u0026 advance Modular + Functional JS patterns. Hopefully I can interest some of the skeptics along the way :crossed_fingers:\n\n-----------\n\n### Please Star this project ❤️\n\n--------------\n\n## Wiki Contents\n\n### [Wiki Main](https://github.com/justsml/escape-from-callback-mountain/wiki)\n\n* Steps\n  1. [Step 1: Break Up The Big Functions](https://github.com/justsml/escape-from-callback-mountain/wiki/Step-1:-Break-Up-The-Big-Functions)\n  1. [Step 2: DRYer Code](https://github.com/justsml/escape-from-callback-mountain/wiki/Step-2:-DRYer-Code)\n  1. [Step 3: Cleanup Code](https://github.com/justsml/escape-from-callback-mountain/wiki/Step-3:-Post-Cleanup)\n* Related Articles\n  * [Finding Promise Anti-patterns](https://github.com/justsml/escape-from-callback-mountain/wiki/Beating-a-dead-horse%3F)\n  * [What about Async, Await and Generators?](https://github.com/justsml/escape-from-callback-mountain/wiki/What-about-Async,-Await-and-Generators%3F)\n* [Examples](https://github.com/justsml/escape-from-callback-mountain/blob/master/examples/)\n  * [**HTTP Client Work Queue**](https://github.com/justsml/escape-from-callback-mountain/wiki/Example:-HTTP-Client-Work-Queue)\n  * [**Typed Errors**](https://github.com/justsml/escape-from-callback-mountain/blob/master/examples/typed-errors/auth.js#L18-L33)\n  * [**S3 File API w/ Express Middleware \u0026 Class wrapper**](https://github.com/justsml/escape-from-callback-mountain/tree/master/examples/functional-s3-block-store)\n\n-----------\n\n### Credits \u0026 Inspiration\n\nI highly recommend reading (or watching) every single link here.\n\n- Tom Stuart's Purely Amazing [Programming with Nothing](http://codon.com/programming-with-nothing)\n- Eric Elliot's Tour de force: [The Two Pillars of JavaScript](https://medium.com/javascript-scene/the-two-pillars-of-javascript-ee6f3281e7f3)\n- Ashley Williams Speaks The Modular Tructh in [A Brief History of Modularity | JSConf EU 2017](https://youtu.be/vypCsVm5z28)\n- And Eric's [Rise and Fall and Rise of Functional Programming (Composing Software)](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c) is damn good too ;)\n- Martin Fowler expertly lays down the Ruby + Haskell pipeline in [Collection Pipeline](https://martinfowler.com/articles/collection-pipeline/)\n\n\n------------\n\n\n#### v2.0 Released\n### Escape from Callback Mountain Key Updates\n\n1. README now more focused on the _Functional River_ pattern.\n    * Counter-examples are still included in `./src`, just not featured on README.\n1. There's an updated production-ready library [`Functional Promises`](https://github.com/functional-promises/functional-promises) which grew out of the feedback \u0026 research from this Project.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjustsml%2Fescape-from-callback-mountain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjustsml%2Fescape-from-callback-mountain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjustsml%2Fescape-from-callback-mountain/lists"}