Tech Talk #1: “Fatal Error – Highway to Hell” Part 2

We have already discussed definition of fatal errors in software engineering, their types (operational and programmer), origins and solutions in previous article. In this part we will share error handling techniques with examples. 

To handle errors or abnormal conditions that occur in applications we implement different techniques, assertions and error handling techniques, depending on particular circumstances.  Such tactics enable management of faults in both Development and Production code.

Assertions are applied to cope with errors that should never appear in code. Error handling techniques are used to handle errors that you except to occur.  We proceeded with naming some error handling options and discussing them in more details.

Error Handling Patters:

  • Return a neutral value
var DEFAULT_GREETING = 'Hello';
var GREETING_PHRASES = ['good morning', 'good evening', 'howzit', 'sup'];

function sayHello(name) {
  var greeting = '';
  try {
    greeting = getGreetingPhrase(GREETING_PHRASES);
  } catch(err) {
    greeting = DEFAULT_GREETING;
  }
  
  console.log(greeting + ', ' + name);
}

function getGreetingPhrase(inputPhrases) {
  if(!(inputPhrases && inputPhrases.length)) {
    throw new Error('no greeting phrases provided');
  }
  return inputPhrases[Math.floor(Math.random()*inputPhrases.length)];
}

sayHello('James Heatfield');

//sup, James Heatfield
var DEFAULT_GREETING = 'Hello';
var GREETING_PHRASES = ['good morning', 'good evening', 'howzit', 'sup'];

function sayHello(name) {
  var greeting = '';
  try {
    greeting = getGreetingPhrase();
  } catch(err) {
    greeting = DEFAULT_GREETING;
  }
  
  console.log(greeting + ', ' + name);
}

function getGreetingPhrase(inputPhrases) {
  if(!(inputPhrases && inputPhrases.length)) {
    throw new Error('no greeting phrases provided');
  }
  return inputPhrases[Math.floor(Math.random()*inputPhrases.length)];
}

sayHello('James Heatfield');

//Hello, James Heatfield
  • Substitute the next piece of valid data

Valid data

  • Return the same answer as previous time
function generateChars() {
  var output    = '';
  var letters   = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  var newChar   = '';
  var charIndex = 0;

  for( var i = 0; i < 5; i++ ) {
    charIndex = Math.floor(Math.random() * letters.length);
    newChar = letters.charAt(charIndex);
    output += newChar;
  }
  return output;
}

var previousResult = '00000';

function getData() {
  var output = '';

  try {
    output = getCharData();
    previousResult = output;
  } catch(err) {
    output = previousResult;
  }
  return output;
}
function getCharData() {
  var err = !(Math.random() >= 0.5); //error simulation
  if(err) {
    throw Error('no data gathered');
  }
  return generateChars();
}
for(var i = 0; i < 7; i++) {
  console.log(getData());
}

//00000 -> error
//ZMr6q
//d9MFW
//d9MFW -> error
//N5yqs
//ESirp
//ESirp -> error
  • Substitute the closest legal value
var SPEED_MIN = 0;
var SPEED_MAX = 150;

//inputSpeed from sensor
function getSpeed(inputSpeed) {
  if(inputSpeed < SPEED_MIN) {
    return SPEED_MIN;
  }

  if(inputSpeed > SPEED_MAX) {
    return SPEED_MAX;
  }

  return inputSpeed;
}

console.log(getSpeed(45));
console.log(getSpeed(-10));
console.log(getSpeed(260));
console.log(getSpeed(150));

//45
//0
//150
//150
  • Log a warning message to a file
var SPEED_MIN = 0;
var SPEED_MAX = 150;

//inputSpeed from sensor
function getSpeed(inputSpeed) {
  if(inputSpeed < SPEED_MIN || inputSpeed > SPEED_MAX) {
    log(new Error('invalid speed value'));
  }
  return inputSpeed;
}

function log(errorMessage) {
  console.log(new Date() + ': ' + errorMessage);
}

console.log(getSpeed(45));
console.log(getSpeed(-10));
console.log(getSpeed(260));

//45

//Wed Nov 04 2015 01:16:33 GMT+0200 (EET): Error: invalid speed value
//-10

//Wed Nov 04 2015 01:16:33 GMT+0200 (EET): Error: invalid speed value
//260
  • Return an error code
var SPEED_MIN = 0;
var SPEED_MAX = 150;

//inputSpeed from sensor
function getSpeed(inputSpeed) {
  if(inputSpeed < SPEED_MIN || inputSpeed > SPEED_MAX) {
    throw { code: 100, message: 'invalid speed value' };
  }
  return inputSpeed;
}

try {
  getSpeed(-10);
} catch(err) {
  //doSomething
}

//{ code: 100, message: 'invalid speed value' }
  • Call an error-processing routing
var SPEED_MIN = 0;
var SPEED_MAX = 150;

//inputSpeed from sensor
function getSpeed(inputSpeed) {
  if(isNaN(inputSpeed)) {
    throw {message: 'invalid speed value'};
  }

  if(inputSpeed < SPEED_MIN) {
    throw {code: 100, message: 'invalid speed value'};
  }

  if(inputSpeed > SPEED_MAX) {
    throw {code: 200, message: 'invalid speed value'};
  }

  return inputSpeed;
}
function errorHandler(err) {
  switch(err.code) {
    case 100:
      console.log('Error 100');
      break;
    case 200:
      console.log('Error 200');
      break;
    default:
      console.log('Oops');
      break;
  }
}
try {
  getSpeed('-10');
} catch(err) {
  errorHandler(err);
}
//Error 100
try {
  getSpeed('360');
} catch(err) {
  errorHandler(err);
}
//Error 200
try {
  getSpeed('doh');
} catch(err) {
  errorHandler(err);
}
//Oops
  • Display an error message
function getGreetingPhrase() {
  var PHRASES = ['Good Morning', 'Sup', 'Hello'];
  var err = !(Math.random() >= 0.5); //error simulation
  if(err) {
    console.log('Error:  Failed to generate greeting phrase');
    return false;
  }
  return PHRASES[Math.floor(Math.random()*PHRASES.length)];
}

function getRandomName() {
  var NAMES = ['James', 'John', 'Doug'];
  var err = !(Math.random() >= 0.5); //error simulation
  if(err) {
    console.log('Error:  Failed to get random name');
    return false;
  }
  return NAMES[Math.floor(Math.random()*NAMES.length)];
}
function getContactFullPhrase(greeting, name) {
  var err = !(Math.random() >= 0.5); //error simulation
  if(err) {
    console.log('Error:  Failed to concat full phrase');
    return false;
  }
  return greeting + ', ' + name;
}

function sayHello() {
  var full_phrase = '';
  var name        = getRandomName();
  var greeting    = getGreetingPhrase();

  if(name && greeting) {
    full_phrase = getContactFullPhrase(greeting, name);
  }

  if(full_phrase) {
    console.log(full_phrase);
  }
}

sayHello();

//Error:  Failed to generate greeting phrase
//Error:  Failed to get random name
//Hello, John
  • Handle the error locally
function getGreetingPhrase() {
  var PHRASES = ['Good Morning', 'Sup', 'Hello'];
  var err = !(Math.random() >= 0.5); //error simulation
  if(err) {
    console.log('ERROR: ', err.message || 'Oops');
  }
  return PHRASES[Math.floor(Math.random()*PHRASES.length)];
}

function getRandomName() {
  var NAMES = ['James', 'John', 'Doug'];
  var err = !(Math.random() >= 0.5); //error simulation
  if(err) {
    console.log('ANOTHER WAY OF HANDLING: ', err.code || '100');
  }
  return NAMES[Math.floor(Math.random()*NAMES.length)];
}

getGreetingPhrase();
getRandomName();

//ERROR:  Oops
//ANOTHER WAY OF HANDLING:  100
  • Shut down the system
function getGreetingPhrase() {
  var PHRASES = ['Good Morning', 'Sup', 'Hello'];
  var err = !(Math.random() >= 0.5); //error simulation
  if(err) {
    process.exit();
  }
  return PHRASES[Math.floor(Math.random()*PHRASES.length)];
}

getGreetingPhrase();

Roman placed emphasis on careful selection of error handling pattern in each particular case, because not all of them may be suitable for your application.

For the finale,  we talked about exceptions, abnormal conditions that require specific processing and often change program execution workflow. Initially there are three core methods for a function to return errors:

  • Throw
function lift(direction) {
  var DIRECTION_UP 	= 'up';
  var DIRECTION_DOWN 	= 'down';

  switch(direction) {
    case DIRECTION_UP:
      //doSomething
      break;
    case DIRECTION_DOWN:
      //doSomething
      break;
    default:
      throw new Error('invalid direction');
  }
}

try {
  lift('toHell');
} catch(err) {
  console.log(err);
}

//[Error: invalid direction]
  • Callback
function lift(direction, callback) {
  var DIRECTION_UP 	= 'up';
  var DIRECTION_DOWN 	= 'down';

  switch(direction) {
    case DIRECTION_UP:
      callback(null, DIRECTION_UP);
      break;
    case DIRECTION_DOWN:
      callback(null, DIRECTION_DOWN);
      break;
    default:
      callback(new Error('invalid direction'), false);
  }
}


lift('toHell', function(err, result) {
  if(err) {
    //doSomethingWithError
  }
  //handle result
});
  • EventEmitter
var events        = require('events');
var eventEmitter  = new events.EventEmitter();

function handleErrors(err) {
  console.log('Oops, something went wrong!');
  console.log(err);
}

eventEmitter.on('appException', handleErrors);


function lift(direction) {
  var DIRECTION_UP 	= 'up';
  var DIRECTION_DOWN 	= 'down';

  switch(direction) {
    case DIRECTION_UP:
      //doSomething
      break;
    case DIRECTION_DOWN:
      //doSomething
      break;
    default:
      eventEmitter.emit('appException', new Error('invalid direction'));
  }
}

lift('toHell');

//Oops, something went wrong!
//[Error: invalid direction]

Roman also highlighted that:

  • try-catch doesn’t work with asynchronous function
  • in a callback flow error should be passed as a first argument
  • event emitter way increases amount of dependencies between different code blocks or modules

At the close of tech talk we discussed working examples and concluded with “questions-answers” section. In general the event was very dynamic, relevant and useful. The audience was deeply involved right from the first slide.  

↑ Go up

Similar post