A Result class for functions that may return errors

Josh Tumath josh.tumath at outlook.com
Tue Oct 18 18:37:53 UTC 2016


At the moment, when we call a synchronous function that runs some kind of operation, there are two obvious ways of returning whether there has been an error: returning a boolean or throwing an error.

Returning a boolean to show whether the operation worked is simple, but doesn't give any indication as to why an operation may have failed (if it's not obvious). For example:

```javascript
if (!bigComplicatedDataStore.add(someObject)) {
    console.log('There has been an error.')
}
```

On the other end of the spectrum, throwing errors allows us to be much more verbose about the error. However, there are overheads associated with this, and many good practices discourage using throwable errors. Additionally, as JavaScript is dynamically typed, the try..catch syntax will inherently catch every type of error; not specifically the errors we want to catch.

```javascript
try {
    bigComplicatedDataStore.add(someObject);
} catch (error) {
    console.log('Oh no! ' + error.message);
}
```

I’m inspired by languages like Rust in how they have tried to reach a middle ground between these two methods of error handling, where a detailed object or enum is returned that includes either the correct result or information about the error. This is a simple way to return error results without the overheads of throwing Errors.

Rust has two enums for this purpose: `Option` and `Result` (https://doc.rust-lang.org/book/error-handling.html#the-option-type). The additional benefit of using this method for handling errors in Rust is that, at compile time, the Rust compiler will force you to handle the error. While JavaScript would not be able to do that, there would still be an advantage to having some kind of Result object in the standard library. For example:

```javascript
function add(data) {
    if (isNotAnObject(data)) {
        return new Result('error', new Error('The data is not an object.'));
    }

    return new Result('success');
}

const result = add(someObject);
if (result.success) {
    console.log('Hooray!');
}

if (result.error) {
    console.log(result.error.message);
}
```

My design of the `Result` object isn't great, but it's just an example of how this could work. This allows us to be verbose in explaining our errors, certain about where the error originates from and leaves us without a large overhead.

Encapsulating error handling in an object, like in the examples above, enables possibilities of introducing new syntax in the future for handling such errors. (This was the case with Promises, which was simplified when the new async..await syntax was introduced.)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20161018/d46c9f7b/attachment.html>


More information about the es-discuss mailing list