2.0.3 • Published 8 years ago

destruction v2.0.3

Weekly downloads
3
License
ISC
Repository
github
Last release
8 years ago

destruction.js

Harness the power of destructuring assignment to simplify error handling in coroutines.

var destruct = require('destruction')
  .setCoroutine(require('bluebird').coroutine)
  .destruct;

var example = destruct(function* () {

  var [err, result] = yield asyncOperation();
  if (err) {
    return console.error(err);
  }
   
  doStuffWith(result);
});

This contrasts with the approach taken by most coroutine implementations: exceptions. It generally looks something like this.

var example = makeCoroutine(function* () {
  try {
    
    var result = yield asyncOperation();
    doStuffWith(result);
        
  } catch (err) {
    console.error(err);
  }
});

Inspired largely by the Go programming language, destruction.js provides a wrapper for other coroutine implementations and makes coroutines truly analagous to nested callbacks and simplifies code if different errors require different responses. For example, here is an implementation with exceptions.

var example = makeCoroutine(function* () {
  var a, b, c;
   
  try {
    a = yield asyncOperationA();
  } catch (err) {
    return handleErrorA(err);
  }
    
  try {
    b = yield asyncOperationB();
  } catch (err) {
    return handleErrorB(err);
  }
    
  try {
    c = yield asyncOperationC();
  } catch (err) {
    return handleErrorC(err);
  }
    
  doStuffWith(a, b, c);
});

Here is how it is done with destruction.js.

var example = destruct(function* () {

  var [err, a] = yield asyncOperationA();
  if (err) return handleErrorA(err);
    
  var [err, b] = yield asyncOperationB();
  if (err) return handleErrorB(err);
    
  var [err, c] = yield asyncOperationC();
  if (err) return handleErrorC(err);
    
  doStuffWith(a, b, c);
});

What exactly is destructuring?

For the uninitiated, destructuring is a new feature of ECMAScript 6, the latest version supported by node.

var [a, b] = [1, 2];
// a is 1
// b is 2

This is the syntax used in destruction.js. The destruct function passes an array of form [err, null] or [null, result] to the generator function. Destructuring assignment can then be used to concisely capture both return values. It is supported by default in node 6 and above and can be enabled with the --harmony flag in some earlier versions, including 4.4.5 LTS.

Destructuring can also be used on objects.

var obj = { valueA: 1, valueB: 2 };
var { valueA: a, valueB: b } = obj;
// a is 1
// b is 2

You can read more about destructuring assignment in ES6 here.

Differences between destruction.js and try...catch

In javascript, try...catch blocks catch errors like trying to call a function that does not exist.

try {
  'some string'.nonexistentFunction();
} catch (err) {
  console.log('this will be printed');
}

This means that if you wrap your coroutine in a try...catch block, a typo in a function name will be treated the same as something like trying to read from a file that does not exist, so it is more difficult to distinguish between the two. In destruction.js, err is only given a value if there is an error in the promise passed via yield, so it will not capture typos. However, in many cases, like if you are using a coroutine as a handler for a route in a web server, you do want to catch these kinds of errors to prevent the entire server from crashing. You can still do this by wrapping the coroutine in a try...catch block like with traditional coroutines. You can gracefully handle programmer mistakes in production and distinguish them from errors that are expected. An error handler can be defined that will catch any uncaught errors.

var destruct = require('destruction')
  .setCoroutine(require('bluebird').coroutine)
  .setErrorHandler(function(err) {
    console.error('there was an error');
    console.error(err);
  })
  .destruct;

Do not use let

ES6 also introduced the let keyword to replace var. It is more strict than var and eliminates some of the odd behavior of javascript variables. However, this does not work well with destruction.js when there are multiple calls in the same coroutine that both use err as an identifier.

let [err, result1] = yield asyncOperation1();
let [err, result2] = yield asyncOperation2();

This code will throw a TypeError because the first line declares err, and the second one declares it once again. The let keyword does not permit this. But what if the second let is removed?

let [err, result1] = yield asyncOperation1();
[err, result2] = yield asyncOperation2();

The TypeError is gone, but since neither var nor let was used, result2 is a global variable, which is not good. If var is used instead of let, err can be used for both without declaring any global variables.

var [err, result1] = yield asyncOperation1();
var [err, result2] = yield asyncOperation2();

Although I like using let whenever I can, it is recommended to use var for these declarations in case err is declared multiple times.

2.0.3

8 years ago

2.0.2

8 years ago

2.0.1

8 years ago

2.0.0

8 years ago

1.0.1

8 years ago

1.0.0

8 years ago