JavaScript is multi-paradigm and provides (at least) 3 different error-handling strategies for synchronous code.
I have seen this question asked in the context of async functions, for which promises are clearly recommended, but what about synchronous functions? 
One example would be parsing. Suppose I have a function for parsing strings into integers. This function will fail in the case of a malformed input, but how should I model this?
1. Try-catch Style
const f = () => {
  throw new Error('Something has gone wrong. ');
};
// Usage
try {
  console.log(f()); 
} catch (error) {
  console.error(error); 
}
2. Callback Style
const f = (callback) => {
  callback(new Error('Something has gone wrong. '), null);
};
// Usage
f((error, result) => {
  if (error) {
    console.error(error);
  } else {
    console.log(result);
  }
});
3. FP Style (also seen in Go)
const f = () => {
  return {
    error: new Error('Something has gone wrong. '), 
    result: null
  };
};
// Usage
const { error, result } = f();
if (error) {
  console.error(error); 
} else {
  console.log(result);
}
 
    