Home

Catch Errors with Try/Catch

The try/catch syntax helps a (control flow)[js/control-flow] detail, where code "lives" in a few different parts:

Connecting Errors To Try/Catch

Catching An Error

console.log('1')
try {
  console.log('2')
  noFunctionYet();
} catch (error) {
  console.log('3')
  console.error(error);
} finally{
  console.log('4')
  console.log('finally run here')
}
console.log('5')

This will output...

1
2
3
ReferenceError: noFunctionYet is not defined
    at Object.<anonymous> (<file-path>/<filename>..js:4:3)
    at Module._compile (node:internal/modules/cjs/loader:1254:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
    at Module.load (node:internal/modules/cjs/loader:1117:32)
    at Module._load (node:internal/modules/cjs/loader:958:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47
4
finally run here
5

Skipping The Catch

function workingFunction(){
  return 2
}
console.log('1')
try {
  console.log('2')
  workingFunction();
} catch (error) {
  console.log('3')
  console.error(error);
} finally{
  console.log('4')
  console.log('finally run here')
}
console.log('5')

This will output...

1
2
4
finally run here
5

Skipping The Finally

function workingFunction(){
  return 2
}
console.log('1')
try {
  console.log('2')
  workingFunction();
} catch (error) {
  console.log('3')
  console.error(error);
}

console.log('4')

This will output...

1
2
4

Handling Errors

Errors can be caught in the code and handled.

// constants
const NOT_NUMBER_ERR_CODE = 'NOT_A_NUMBER';
const TOO_LOW_ERR_CODE = 'TOO_LOW';



// error classes
class NotANumber extends Error{
  constructor (num) {
    super(num + ' must be a number')
  }
  get name () { return 'NotANumber' }
  get code () { return NOT_NUMBER_ERR_CODE }
}

class TooLow extends Error {
  constructor(num) {
    super(num + ' must be above zero');
  }
  get name() {
    return 'TooLow';
  }
  get code() {
    return TOO_LOW_ERR_CODE;
  }
}




// logging function example
function logErrorDetails(e) {
  if (e.code == NOT_NUMBER_ERR_CODE) console.log('caught a wrong type');
  if (e.code == TOO_LOW_ERR_CODE) console.log('caught a too-low err');
}



// the function being tested below!
function addTwo(a, b) {
  if (typeof a !== 'number') throw new NotANumber(a);
  if (typeof b !== 'number') throw new NotANumber(b);

  if (a < 0) throw new TooLow(a);
  if (b < 0) throw new TooLow(b);
  return a + b;
}





// perhaps the "meaningful" control-flow of the code
console.log('1');
try {
  console.log('2');
  addTwo('test', -3);
} catch (error) {
  logErrorDetails(error)
} finally {
  console.log('4');
  console.log('finally run here');
}
console.log('5');



try {
  console.log('6');
  addTwo(-3,2);
} catch (error) {
  logErrorDetails(error);
} finally {
  console.log('7');
  console.log('finally run here');
}

this will return

1
2
caught a wrong type
4
finally run here
5
6
caught a too-low err
7
finally run here

Be Aware Of Code That Runs Later

This is mostly identical to the previous code example.
Here, though, in the try/catch block, a setTimeout causes the function call to "wait" 2 seconds, triggering a throw that is NOT caught by the catch block:

const NOT_NUMBER_ERR_CODE = 'NOT_A_NUMBER';
const TOO_LOW_ERR_CODE = 'TOO_LOW';



// error classes
class NotANumber extends Error{
  constructor (num) {
    super(num + ' must be a number')
  }
  get name () { return 'NotANumber' }
  get code () { return NOT_NUMBER_ERR_CODE }
}

class TooLow extends Error {
  constructor(num) {
    super(num + ' must be above zero');
  }
  get name() {
    return 'TooLow';
  }
  get code() {
    return TOO_LOW_ERR_CODE;
  }
}




// logging function example
function logErrorDetails(e) {
  if (e.code == NOT_NUMBER_ERR_CODE) console.log('caught a wrong type');
  if (e.code == TOO_LOW_ERR_CODE) console.log('caught a too-low err');
}



// the function being tested below!
function addTwo(a, b) {
  if (typeof a !== 'number') throw new NotANumber(a);
  if (typeof b !== 'number') throw new NotANumber(b);

  if (a < 0) throw new TooLow(a);
  if (b < 0) throw new TooLow(b);
  return a + b;
}





// perhaps the "meaningful" control-flow of the code
console.log('1');
try {
  console.log('2');
  setTimeout(() => {
    addTwo('test', -3);
  }, 2000)
} catch (error) {
  logErrorDetails(error)
} finally {
  console.log('3');
  console.log('finally run here');
}
console.log('4');



try {
  console.log('5');
  addTwo(-3,2);
} catch (error) {
  logErrorDetails(error);
} finally {
  console.log('6');
  console.log('finally run here');
}

this will return

1
2
3
finally run here
4
5
caught a too-low err
6
finally run here
<path-to>/<the-filename>.js:181
  if (typeof a !== 'number') throw new NotANumber(a);
                             ^

NotANumber: test must be a number
    at addTwo (<path-to>/<the-filename>.js:181:36)
    at Timeout._onTimeout (<path-to>/<the-filename>.js:198:5)
    at listOnTimeout (node:internal/timers:569:17)
    at process.processTimers (node:internal/timers:512:7)

Node.js v18.14.2
Tags: