Step 2: DRYer Code

Daniel Levy edited this page Aug 16, 2017 · 7 revisions

Detailed Steps

Required Tasks

  1. Promisify Callback-based libs/dependencies w/ Bluebird.promisify, Bluebird.promisifyAll, or denodify.
  2. Replace all occurrences of callback(...) with either a return Promise.reject(err) for errors, or anything else for success (Promises and values are handled the same).
  3. Ensure every function uses return at least once.
  4. Remember you can start a Promise-chain with literals or variables using Promise.resolve(user).
  5. If you don't have a 'starting' value/parameter you can always .resolve() w/o params. Like so: () => Promise.resolve().then(lookupGps).

This may look like a lot of change for a single step, however all these steps are required before your code improves.

The first few times you go through this process it'll seem fraught with peril. With a bit of practice, it actually becomes a very speedy process. I used to take 3+ hours converting a 500-line file. Now the same size task takes about 15-30 minutes. See Common Issues section below.

Recommended Tasks - Code Clarity

  1. Use destructuring for function params. This makes it easier to understand exactly what a function needs, which should make it's purpose clear (assuming a reasonable name). const auth = ({user, pass}) => {}
  2. Conversely, finding the returns in the function should let you quickly reason about the code-execution-path.

The previous 2 steps have a way of forcing you to write focused single-purpose functions.

Common Issues

I understand if Promises are maddening at first.

I used to run into 2 issues seemingly constantly: no visibility into errors and forgetting to return.

It turns out they are potentially related issues.

  • These issues: 'silent exit with or w/o error' or 'empty return value' or hanging .then() have 2-3 general causes:
    • 95% of the time: Failed to return from function.
    • 4% of the time it's a complicated hierarchy - say +3 Promise chains deep. This will resemble callback hell. Don't do this - either break apart the 3 Promise chains, or flatten it out into 1 promise declaration. A good Promise nesting rule-of-thumb: only nest a new Promise 'chain' when inside a collection method callback (i.e. Bluebird's .map, or .mapSeries).
    • 1% it is a bug in calling Promise.reject(new Error) requires valid Error instance. Avoid throwing errors - while bluebird is pretty awesome at handling this, it's too easy to throw from a different async context. The result can be a meaningless stack traces. To get some hint as to where it went haywire enable LongStackTraces, or promisify the async code you're calling & plug it into your Promise chain.

Make sure to read the install guide for Bluebird - look for long stack trace support.

View Diff of PR

Preview of Diff:


You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.