You're reading the Keybase blog.
There are more posts.
                When you're done, you can install Keybase.
Error Handling in JavaScript
Chris Coyne and I wrote IcedCoffeeScript (ICS) almost 3 years ago, and have been developing with it almost exclusively since. Our current project, Keybase.io uses ICS everywhere: on the Web front-end, in our node command-line client, and on the server back-end. As client code becomes more complex, we find the features of ICS more crucial to getting our job done. I thought I'd summarize what makes it a nice system for those who haven't checked it out.
Background
  IcedCoffeeScript is a fork of CoffeeScript that introduced two new keywords
  --- await and defer.  Internally, it augments the CoffeeScript compiler
  with a Continuation-Passing Style (CPS) code rewriter.
  So the compiler outputs "pyramid-of-death" style spaghetti JavaScript code, while the  programmer sees clean straightline CoffeeScript-like code.
For instance, consider this common pattern in node.js. I want to make two serial HTTP requests, and the first depends on the second. When it's all done, I want a callback to fire with the results, or to describe that an error happened. Here's the standard CoffeeScript way:
get2 = (cb) ->
  request "https://x.io/", (err, res, body) ->
    if err? then cb err
    else
      request "https://x.io/?q=#{body.hash}", (err, res, body) ->
        if err? then cb err
        else cb null, body
  This is not a contrived example, it was literally the first one I thought of.
  I hate this code for several reasons: it's hard to read; it's hard refactor; there are
  repeated calls to cb, any one of which might be forgotten and can break the program;
  it's brittle and won't compose well with standard language features, like if and for.
Cool that Coffee Down
The first part of the solution came intentionally with IcedCoffeeScript; use CPS conversion to achieve the illusion of threads:
get2 = (cb) ->
  await request "https://x.io/", defer(err, res, body)
  unless err?
    await request "https://x.io/?q=#{body.hash}", defer(err, res, body)
  cb err, body
  It's not crucial here to understand the finer points of await and defer, but the
  salient aspects are that the function get2 blocks until request completes,
  at which point err, res, body will get the three values that request called back with. Then, control continues at unless err?.
  Already, this code is much cleaner.  There's only one call to cb; the code doesn't
  veer off the page to the right; adding conditionals and iteration would be straightforward.
  But there's still an issue: errors are ugly to handle.  If we had 4 steps in this
  request pipeline, we'd need three checks of err? to make sure there wasn't an error.
  Or something equally gross:
get2 = (cb) ->
  await request "https://x.io/", defer(err, res, body)
  return cb err if err?
  await request "https://x.io/?q=#{body.hash}", defer(err, res, body)
  return cb err if err?
  # etc...
  cb null, res
An Elegant Solution that Exploits the CPS-conversion
  An elegant solution was discovered
  a year into writing code with IcedCoffeeScript.
  In the above example, the language feature defer(err, res, body) creates a callback that
  request calls when it's done.  That callback represents the rest of the get2 function!
  Meaning, if there's an error, it can be thrown away, since the rest of the function should
  not be executed.  Instead, the outer callback, cb, should be called with an error.
We can accomplish this pattern without language additions, just with library help:
{make_esc} = require 'iced-error'
get2 = (cb) ->
  esc = make_esc cb # 'ESC' stands for Error Short Circuiter
  await request "https://x.io/", esc(defer(res, body))
  await request "https://x.io/?q=#{body.hash}", esc(defer(res, body))
  cb null, body
  If the first call to request calls back with an error, then cb is called with the error, and the function is over.
  Otherwise, onto the second request (and the rest of get2).
  The function make_esc might seem magical, but it's not, it's doing something quite simple:
make_esc = (cb1) -> (cb2) -> (err, args...) ->
  if err? then cb1(err) else cb2(args...)
  It takes the original callback, and returns a function that we've called esc.  In turn,
  esc takes the local callback, and returns a third callback.  This third callback is what
  request calls when it's done.  If the result is an error, then fire the outer callback
  with the error, throwing away cb2, which represents the rest of the function. If the result
  is not an error, then forge ahead.  Call cb2 which executes the rest of the function.
  This make_esc function is extremely useful and I use it in almost every function that I write.
  But because it's a library function, you can write your own to work around the error
  semantics of your particular library, without having to fiddle with the IcedCoffeeScript
  compiler. Or, you can write an make_esc that first releases a lock, and then calls cb
  (also quite useful).  Whatever ought to happen on error but before the function scope
  disappears, a short-circuiter can handle it cleanly.
In Sum...
IcedCoffeeScript plus the "Error Short-Circuiter" pattern is a powerful and succinct way to clean up your JavaScript-based applications. We've been writing code this way for over a year now and can't imagine going back to the old toolset.
This is a post on the Keybase blog.
- Keybase joins Zoom
- New Cryptographic Tools on Keybase
- Introducing Keybase bots
- Dealing with Spam on Keybase
- Keybase SSH
- Stellar wallets for all Keybase users
- Keybase ♥'s Mastodon, and how to get your site on Keybase
- Keybase is not softer than TOFU
- Cryptographic coin flipping, now in Keybase
- Keybase exploding messages and forward secrecy
- Keybase is now supported by the Stellar Development Foundation
- New Teams Features
- Keybase launches encrypted git
- Introducing Keybase Teams
- Abrupt Termination of Coinbase Support
- Introducing Keybase Chat
- Keybase chooses Zcash
- Keybase Filesystem Documents
- Keybase's New Key Model
- Keybase raises $10.8M
- The Horror of a 'Secure Golden Key'
