Skip to content

Instantly share code, notes, and snippets.

@isaacs
Last active June 9, 2022 10:45
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save isaacs/7269994 to your computer and use it in GitHub Desktop.
Save isaacs/7269994 to your computer and use it in GitHub Desktop.
// Some bug in the code causes it to crash sometimes.
// Gee, that sure is annoying! Server restarts in a
// clean state, and spams the error log until we go
// in and fix it.
function doSomeShiz(n) {
shiz.expect(n);
for (var i = 0; i < n; i++) {
shiz();
}
}
var shiz = (function() {
// this is some internal state etc.
var theShiz = 0;
var expectShiz = 0;
function expect(n) {
expectShiz = n;
theShiz = 0;
}
function verify() {
console.log('did %d of shiz, expected %d', theShiz, expectShiz);
console.log('cleanup shiz');
theShiz = 0;
}
function shiz() {
theShiz++;
// oops
// such error
// many throw uncawt lul
if (Math.random() > 0.5)
throw new Error('shank shiz');
if (theShiz === expectShiz)
verify();
if (theShiz > expectShiz)
throw new Error('uh o so error such unexpect');
}
shiz.verify = verify;
shiz.expect = expect;
return shiz;
})()
doSomeShiz(10);
// I know! Since shiz() is throwing, just keep trying!
// But make sure we don't accidentally catch some OTHER
// kind of error, because that could probably be bad, right?
// Like, if it's actually something important.
function doSomeShiz(n) {
shiz.expect(n);
for (var i = 0; i < n; i++) {
try {
shiz();
} catch (er) {
// if it throws the error we expect, back up and try again
// even this isn't perfect, because it can throw the error we DONT expect,
// and we probably don't want to catch that one!
if (er.message === 'shank shiz')
i--;
else
throw er;
}
}
}
var shiz = (function() {
// this is some internal state etc.
var theShiz = 0;
var expectShiz = 0;
function expect(n) {
expectShiz = n;
theShiz = 0;
}
function verify() {
console.log('did %d of shiz, expected %d', theShiz, expectShiz);
console.log('cleanup shiz');
theShiz = 0;
}
function shiz() {
theShiz++;
// oops
// such error
// many throw uncawt lul
if (Math.random() > 0.5)
throw new Error('shank shiz');
if (theShiz === expectShiz)
verify();
if (theShiz > expectShiz)
throw new Error('uh o so error such unexpect');
}
shiz.verify = verify;
shiz.expect = expect;
return shiz;
})()
doSomeShiz(10);
// Turns out, this keeps crashing, because the throw out
// of shiz-land futzes the shiz all up. Let's try a bigger
// error handler!
//
// /Users/isaacs/dev/js/node-master/throw-leaks-local-catch.js:13
// throw er;
// ^
// Error: uh o so error such unexpect
// at shiz (/Users/isaacs/dev/js/node-master/throw-leaks-local-catch.js:47:13)
// at doSomeShiz (/Users/isaacs/dev/js/node-master/throw-leaks-local-catch.js:5:7)
// at Object.<anonymous> (/Users/isaacs/dev/js/node-master/throw-leaks-local-catch.js:56:1)
// at Module._compile (module.js:449:26)
// at Object.Module._extensions..js (module.js:467:10)
// at Module.load (module.js:349:32)
// at Function.Module._load (module.js:305:12)
// at Function.Module.runMain (module.js:490:10)
// at startup (node.js:123:16)
// at node.js:1027:3
// This is even worse!
//
// Now, instead of just crashing, state is fubar!
// There's no way to know how far we got through the
// loop, no way to continue where we left off, and
// the internal state is who knows what.
//
// Practically *by definition* unexpected exceptions
// are going to abruptly jump out of their local
// context in a way that cannot be adequately handled.
//
// In a real application, where presumably the state
// of things actually *matters*, this is the worst
// possible outcome! A crashed process can simply be
// restarted, but an *insane* process might start
// acting in unpredictable ways: latency bubbles,
// incorrect behavior, etc. These are almost impossible
// to debug reasonably, even with the best tools, and
// are a huge drain on user experience and dev time.
//
// Maximum catastrophe! Hit you to death!
process.on('uncaughtException', function(er) {
console.log('air or lawl!', er.message);
});
function doSomeShiz(n) {
shiz.expect(n);
for (var i = 0; i < n; i++) {
shiz();
}
}
var shiz = (function() {
// this is some internal state etc.
var theShiz = 0;
var expectShiz = 0;
function expect(n) {
expectShiz = n;
theShiz = 0;
}
function verify() {
console.log('did %d of shiz, expected %d', theShiz, expectShiz);
console.log('cleanup shiz');
theShiz = 0;
}
function shiz() {
theShiz++;
// oops so unepxect
// such error
// many throw uncawt lul
if (Math.random() > 0.5)
throw new Error('shank shiz'); // resource unvailable, etc.
if (theShiz === expectShiz)
verify();
// What if, instead of simply throwing here, we just kept
// incrementing theShiz as long as it's !== expectShiz?
// Must be an Apple employee, because it's going to be in
// one infinite loop!
if (theShiz > expectShiz)
throw new Error('uh o such error so excetpional!');
}
shiz.verify = verify;
shiz.expect = expect;
return shiz;
})()
doSomeShiz(10);
@isaacs
Copy link
Author

isaacs commented Nov 4, 2013

@rlidwka
Copy link

rlidwka commented Jan 11, 2014

In these examples shiz itself should use try..catch block.

Anyway, the lessons for the audience are:

  • state is awful
  • make stateless functions where it is possible
  • when you have to deal with state, program it keeping in mind that every external call can rewind your stack
  • learn Haskell

I understand if node.js core have a state and can't use try..catch..finally for performance reasons. But when people with no performance constrains write such code, it's their own fault.

PS: express.js wraps all callbacks with try..catch, and nobody complained about it yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment