Skip to content

Commit

Permalink
feat(rethrow): Rethrowing Error instances, especially useful for asyn…
Browse files Browse the repository at this point in the history
…c promise errors, fixes #19
  • Loading branch information
bahmutov committed Feb 19, 2016
1 parent 7a50657 commit 6db46b7
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 15 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,48 @@ Uncaught Error: foo
In this case, there is no meaningful error stack, so use good message
arguments - there is no performance penalty!

## Rethrowing errors

If the condition itself is an instance of Error, it is simply rethrown (synchronously or
asynchronously).

```js
lazyAss(new Error('foo'));
// Uncaught Error: foo
```

Useful to make sure errors in the promise chains are
[not silently ignored](https://glebbahmutov.com/blog/why-promises-need-to-be-done/).

For example, a rejected promise below this will be ignored.

```js
var p = new Promise(function (resolve, reject) {
reject(new Error('foo'));
});
p.then(...);
```

We can catch it and rethrow it *synchronously*, but it will be ignored too (same way,
only one step further)

```js
var p = new Promise(function (resolve, reject) {
reject(new Error('foo'));
});
p.then(..., lazyAss);
```

But we can actually trigger global error if we rethrow the error *asynchronously*

```js
var p = new Promise(function (resolve, reject) {
reject(new Error('foo'));
});
p.then(..., lazyAssync);
// Uncaught Error: foo
```

## Predicate function as a condition

Typically, JavaScript evaluates the condition expression first, then calls *lazyAss*.
Expand Down
8 changes: 8 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
typeof arg === 'boolean';
}

function isError(e) {
return e instanceof Error;
}

function toString(arg, k) {
if (isPrimitive(arg)) {
return JSON.stringify(arg);
Expand Down Expand Up @@ -72,6 +76,10 @@
}

function lazyAssLogic(condition) {
if (isError(condition)) {
return condition;
}

var fn = typeof condition === 'function' ? condition : null;

if (fn) {
Expand Down
30 changes: 15 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "lazy-ass",
"description": "Lazy assertions without performance penalty",
"author": "Gleb Bahmutov <[email protected]>",
"version": "0.0.0-semantic-release",
"author": "Gleb Bahmutov <[email protected]>",
"bugs": {
"url": "https://github.com/bahmutov/lazy-ass/issues"
},
Expand All @@ -19,17 +19,6 @@
"post-merge": []
}
},
"release": {
"verifyConditions": [
{
"path": "@semantic-release/condition-travis"
},
{
"path": "condition-node-version",
"node": "4.2.2"
}
]
},
"contributors": [],
"dependencies": {},
"devDependencies": {
Expand Down Expand Up @@ -89,22 +78,33 @@
],
"license": "MIT",
"main": "index.js",
"release": {
"verifyConditions": [
{
"path": "@semantic-release/condition-travis"
},
{
"path": "condition-node-version",
"node": "4.2.2"
}
]
},
"repository": {
"type": "git",
"url": "https://github.com/bahmutov/lazy-ass.git"
},
"scripts": {
"commit": "git-issues && commit-wizard",
"build": "grunt",
"commit": "git-issues && commit-wizard",
"coveralls": "cat coverage/PhantomJS*/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"demo": "grunt gh-pages",
"dont-break": "dont-break --timeout 30",
"issues": "git-issues",
"mocha": "mocha test/*.spec.js",
"pkgfiles": "pkgfiles",
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
"size": "tarball=\"$(npm pack .)\"; wc -c \"${tarball}\"; tar tvf \"${tarball}\"; rm \"${tarball}\";",
"test": "grunt test",
"watch": "grunt watch",
"semantic-release": "semantic-release pre && npm publish && semantic-release post"
"watch": "grunt watch"
}
}
36 changes: 36 additions & 0 deletions test/lazy-ass.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
(function (root) {
var expect = root.expect;

function hasPromises() {
return typeof Promise !== 'undefined';
}

function noop() {}

describe('lazyAss', function () {
beforeEach(function () {
if (typeof window === 'undefined') {
Expand All @@ -16,6 +22,36 @@
}
});

describe('just an error', function () {
it('rethrows instance of Error as condition', function () {
expect(function () {
lazyAss(new Error('foo'));
}).to.throwException(/^foo$/);
});

if (hasPromises()) {
/* global Promise */
it('rethrows promise errors', function (done) {
var p = new Promise(function (resolve, reject) {
reject(new Error('foo'));
});
p.then(noop, lazyAss).catch(function (err) {
expect(err.message).to.be('foo');
done();
});
});

// really hard to make this test fail, yet pass
// comment out, but leaving it in the code for future
// it('rethrows promise errors', function (done) {
// var p = new Promise(function (resolve, reject) {
// reject(new Error('foo'));
// });
// p.then(noop, lazyAssync);
// });
}
});

describe('lazyAss function itself', function () {
it('is a function', function () {
expect(lazyAss).not.to.be(undefined);
Expand Down

0 comments on commit 6db46b7

Please sign in to comment.