8

Their is already a question on this topic

Node.js Best Practice Exception Handling

which is old and answers are very much outdated, domains have even deprecated since then.

Now in a post Async/Await Node.js scenario shouldn't we consider sync and async cases similarly and throw exceptions in sync functions and rejecting promises in async functions instead of returning an Error instance in the former case.

let divideSync = function(x,y) {
    // if error condition?
    if ( y === 0 ) {
        // "throw" the error 
        throw new Error("Can't divide by zero exception")
    }
    else {
        // no error occured, continue on
        return x/y
    }
}

Simulating async divide operation

let divideAsync = function(x, y) {

  return new Promise(function(resolve, reject) {

    setTimeout(function() {
      // if error condition?
      if (y === 0) {
        // "throw" the error safely by rejecting the promise
        reject (new Error("Can't divide by zero exception"));
      } else {
        // no error occured, continue on
        resolve(x / y)
      }
    }, 1000);
  })

};

So sync and async exceptions can be handled in a uniform manner

let main = async function () {
    try {
        //const resultSync = divideSync(4,0);
        const resultAsync = await divideAsync(4,0);
    }
    catch(ex) {
        console.log(ex.message);
    }

}
Community
  • 1
  • 1
adnan kamili
  • 8,967
  • 7
  • 65
  • 125

1 Answers1

11

The answers from Node.js Best Practice Exception Handling are old and very much outdated

Not that much. This answer, with the list from this well-maintained blog post, is quite up to date.
The offical node.js guide is always a good read, and the general approach hasn't changed that much.

So what has changed?

  • Domains are broken and deprecated. Well, that's old news.
  • The typical "node-style callbacks" with their error-first-parameter, which are fired exactly once, should no more be used. This simple sequential asynchronous coding style with all its problems has been superseeded by promises and async/await. (Note: event emitters etc are a different case)
  • process.on('uncaughtException') is supplemented by process.on('unhandledRejection')
  • promises also catch programmer errors if used correctly. For boring sequential asynchronous code, they can replace domains.

So what does this mean for the common code?

Shouldn't we consider sync and async cases similarly and throw exceptions in sync functions and rejecting promises in async functions instead of returning an Error instance?

Yes, exactly. You should reject your promises with Errors (or throw them from async functions).

Notice you will rarely have to call reject yourself. With promises, you should be able to throw in your code. If you can't, you're likely not using them correctly - and programmer mistakes wouldn't be caught either there.

The golden rule for this code is: Never use callbacks that are not promise callbacks. "Promise callbacks" refers to the new Promise, then, and catch arguments, and possibly some of the custom methods of your library (e.g. finally). This is where your example code above has a problem. Written correctly, it should read

async function divideAsync(x, y) {
    await new Promise(resolve =>
        setTimeout(resolve, 1000) // don't even pass a function expression
    );
    if (y === 0) {
        throw new Error("Can't divide by zero exception");
    } else {
        return x / y;
    }
}
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • So as a rule if an asynchronous function uses async then throw the error and if not then reject the error – adnan kamili Sep 23 '16 at 09:10
  • No, even if a function doesn't use `async` syntax, but employs promises, you can and should `throw` (or `return Promise.reject(…)` if you prefer) from promise callbacks, like `divideAsync = (x, y) => new Promise(r => setTimeout(r, 1000)).then(() => { if (y === 0) throw new Error(…) else return x / y })` – Bergi Sep 23 '16 at 09:16
  • 1
    Why exception is not caught (unhandled) if I throw the error in my version of code – adnan kamili Sep 23 '16 at 09:30
  • Because you're throwing in a `setTimeout` callback, not a promise callback. See also [here](http://stackoverflow.com/q/25143476/1048572) – Bergi Sep 23 '16 at 09:45