Should async function never ever throw? - node.js

I wrote a library with a number of async functions.
A SYNCHRONOUS helper function throws an error if one of the parameters is plain wrong:
proto.makeParameters= function( filters ){
default:
throw( new Error("Field type unknown: " + fieldObject.type ) );
break;
}
In my async functions, when I use it, I have:
proto.someAsyncFunction = function( cb ){
// Run the query
try {
var parameters = this.makeParameters( filters );
} catch( e ){
return cb( e );
}
}
So:
Is it good practice that asyncfunctions should never ever throw? (Like I did)
Right now, I am catching ALL errors. Shall I e more choosy? Maybe make up an error type and just check for that? If so, what should I do in either case?

Your assumptions on the async code are correct. See this post by Isaac Schlueter himself on the topic:
The pattern in node is that sync methods throw, and async methods pass
the error as the first argument to the callback. If the first
argument to your callback is falsey (usually null or undefined), then
all is well with the world.
http://groups.google.com/forum/#!msg/nodejs/W9UVJCKcJ7Q/rzseRbourCUJ

Is it good practice that async functions should never ever throw? (Like I did)
async functions, of course, will throw exceptions whenever we like it not, simply because of the software imperfection. So throwing custom exception is completely fine but what is important is how to correctly catch them.
The problem is that differently from sync code the stack of the async exception may not be available. So when the exception occurs, it is not always possible to say where to return the control and where is the handler. node.js has two methods of specifying what to do when the exception in asynchronous code occurs: process uncaughtException and domains.
As you see, dealing of the exceptions in async code is tricky so throwing an exception should be considered as a last option. If the function just returns status of the operation it is not an exception.
It seems for me that in the provided code fragment the exception is thrown correctly because it indicates that the method was called improperly and cannot do its job. In other words, the error is permanent. This indicates serious flaw in the application that should be fixed. But if the function cannot create a parameter for some temporary reason that can be fixed without modifying the application, then returning the status is more appropriate choice.

Related

Getting UnhandledPromiseRejectionWarning even when catching exceptions

In NodeJS, I have some code like this:
function doSomethingAsync() {
return Promise.reject("error");
}
function main() {
doSomethingAsync().catch();
}
When I run this code, I get an UnhandledPromiseRejectionWarning.
I know that making my calling function async and awaiting doSomethingAsync inside of a try/catch makes the error go away, but in this case, I don't want to add the extra complexity of making the function async and awaiting just to mute an error, so I'd prefer to use catch().
Why doesn't my method of error handling mute the error?
.catch() isn't actually catching anything.
If we look at the docs:
Internally calls Promise.prototype.then on the object upon which it was called, passing the parameters undefined and the received onRejected handler.
specs found here
If we then look at the docs for Promise.then, we find:
onRejected: A Function called if the Promise is rejected. This function has one argument, the rejection reason. If it is not a function, it is internally replaced with a "Thrower" function (it throws an error it received as argument).
So doing .catch() will not actually catch anything and your app will continue throwing an error. You have to pass a function to catch().
Doing this will mute the error:
doSomethingAsync().catch(() => { });
Updating to say that this is actually called out here at the top of the page, but I just missed it.

What does `napi_throw_error` do when called from an asynchronous N-API addon's `napi_async_complete_callback`?

I recently completed making an asynchronous version for all the functions in a pure C API, wrapped with N-API to work with JS/TS as a nodejs addon.
The last problem I had to fix was making sure that C POSIX-style errors (ie, returned integer codes) were transferred correctly to the JS at the end of a worker's execution (with the corresponding string, for which we have both an enum of exceptions, and a list of error messages).
When thrown with napi_throw_error (as I did for the synchronous version of all our calls), within the napi_async_complete_callback, these exceptions were never caught at the JS level (I suppose it was because it was within a different async context; I saw online people having a similar problem with ajax). Instead, I opted to just construct my errors as napi_value types, and return these via napi_reject_deferred. This seemed to have the desired effect, of being caught properly when doing a try { await My_NapiWrapper_XYZ() } catch (ex) { ... }.
So I don't really have a problem to fix, but I AM intrigued. These napi_throw_error thrown errors do probably go somewhere. Though I have no idea where. Where should one look to catch an error thrown with napi_throw_error from a napi_async_complete_callback ? Can you give a code example ?
No, they don't go anywhere. It is a bug that I just opened with them:
https://github.com/nodejs/node/issues/41377
There is a general problem with handling exceptions in asynchronous callbacks. Normally, they cannot be catched and should lead to program termination but Node's developers have decided to try to keep it running when they can.

Catching and recovering from error in C++ function called from Duktape

I have created a plugin for the OpenCPN marine navigation program that incorporates Duktape to provide a scripting capability. OpenCPN uses wxWidgets.
Basically, the plugin presents the user with a console comprising a script window, an output window and various buttons. The user enters their script (or loads it from a .js file) and clicks on Run. The script is run using duk_peval. On return I display the result, destroy the context and wait for the user to run again, perhaps after modifying the script. All this works well. However, consider the following test script:
add(2, 3);
function add(a, b){
if (a == b) throw("args match");
return(a + b);
}
If the two arguments in the call to add are equal. The script throws an error and the user can try again. This all works.
Now I can implement add as a c++ function thus:
static duk_ret_t add(duk_context *ctx){
int a, b;
a = duk_get_int(ctx, 0);
b = duk_get_int(ctx, 1);
if (a == b){
duk_error(ctx, DUK_ERR_TYPE_ERROR, "args match");
}
duk_pop_2(ctx);
duk_push_int(ctx, a+b);
return (1);
}
As written, this passes the error to the fatal error handler. I know I must not try and use Duktape further but I can display the error OK. However, I have no way back to the plugin.
The prescribed action is to exit or abort but these both terminate the hosting application, which is absolutely unacceptable. Ideally, I need to be able to return from the duk_peval call with the error.
I have tried running the add function using duk_pcall from an outer C++ function. This catches the error and I can display it from that outer function. But when I return from that outer function, the script carries on when it should not and the eventual return from the duk_peval call has no knowledge of the error.
I know I could use try/catch in the script but with maybe dozens of calls to the OpenCPN APIs this is unrealistic. Percolating an error return code all the way back, maybe through several C++ functions and then to the top-level script would also be very cumbersome as the scripts and functions can be quite complex.
Can anyone please suggest a way of passing control back to my invoking plugin - preferably by returning from the duk_peval?
I have cracked this at last.
Firstly, I use the following in error situations:
if (a == b){
duk_push_error_object(ctx, DUK_ERR_ERROR, "args match");
duk_thow(ctx);
}
If an error has been thrown, the returned values from duk_peval and duk_pcall are non-zero and the error object is on the stack - as documented
It is all working nicely for me now.

Why does node prefer error-first callback?

Node programmers conventionally use a paradigm like this:
let callback = function(err, data) {
if (err) { /* do something if there was an error */ }
/* other logic here */
};
Why not simplify the function to accept only a single parameter, which is either an error, or the response?
let callback = function(data) {
if (isError(data)) { /* do something if there was an error */ }
/* other logic here */
};
Seems simpler. The only downside I can see is that functions can't return errors as their actual intended return value - but I believe that is an incredibly insignificant use-case.
Why is the error-first pattern considered standard?
EDIT: Implementation of isError:
let isError = obj => obj instanceof Error;
ANOTHER EDIT: Is it possible that my alternate method is somewhat more convenient than node convention, because callbacks which only accept one parameter are more likely to be reusable for non-callback use-cases as well?
(See "Update" below for an npm module to use the callback convention from the question.)
This is just a convention. Node could use the convention that you suggest as well - with the exception that you wouldn't be able to return an error object as an intended value on success as you noticed, which may or may not be a problem, depending on your particular requirements.
The thing with the current Node convention is that sometimes the callbacks may not expect any data and the err is the only parameter that they take, and sometimes the functions expect more than one value on success - for example see
request(url, (err, res, data) => {
if (err) {
// you have error
} else {
// you have both res and data
}
});
See this answer for a full example of the above code.
But you might as well make the first parameter to be an error even in functions that take more than one parameter, I don't see any issue with your style even then.
The error-first Node-style callbacks is what was originally used by Ryan Dahl and it is now pretty universal and expected for any asynchronous functions that take callbacks. Not that this convention is better than what you suggest or worse, but having a convention - whatever it is - make the composition of callbacks and callback taking functions possible, and modules like async rely on that.
In fact, I see one way in which your idea is superior to the classical Node convention - it's impossible to call the callback with both error and the first non-error argument defined, which is possible for Node style callbacks and sometimes can happen. Both conventions could potentially have the callback called twice though - which is a problem.
But there is another widely used convention in JavaScript in general and Node in particular, where it's impossible to define both error and data and additionally it's impossible to call the callback twice - instead of taking a callback you return a promise and instead of explicitly checking the error value in if as is the case in Node-style callbacks or your style callbacks, you can separately add success and failure callbacks that only get relevant data.
All of those styles are pretty much equivalent in what they can do:
nodeStyle(params, function (err, data) {
if (err) {
// error
} else {
// success
}
};
yourStyle(params, function (data) {
if (isError(data)) {
// error
} else {
// success
}
};
promiseStyle(params)
.then(function (data) {
// success
})
.catch(function (err) {
// error
});
Promises may be more convenient for your needs and those are already widely supported with a lot of tools to use them, like Bluebird and others.
You can see some other answers where I explain the difference between callbacks and promises and how to use the together in more detail, which may be helpful to you in this case:
A detailed explanation on how to use callbacks and promises
Explanation on how to use promises in complex request handlers
An explanation of what a promise really is, on the example of AJAX requests
Examples of mixing callbacks with promises
Of course I see no reason why you couldn't write a module that converts Node-style callbacks into your style callbacks or vice versa, and the same with promises, much like promisify and asCallback work in Bluebird. It certainly seems doable if working with your callback style is more convenient for you.
Update
I just published a module on npm that you can use to have your preferred style of callbacks:
https://www.npmjs.com/package/errc
You can install it and use in your project with:
npm install errc --save
It allows you to have a code like this:
var errc = require('errc');
var fs = require('fs');
var isError = function(obj) {
try { return obj instanceof Error; } catch(e) {}
return false;
};
var callback = function(data) {
if (isError(data)) {
console.log('Error:', data.message);
} else {
console.log('Success:', data);
}
};
fs.readFile('example.txt', errc(callback));
For more examples see:
https://github.com/rsp/node-errc-example
I wrote this module as an example of how to manipulate functions and callbacks to suit your needs, but I released it under the MIT license and published on npm so you can use it in real projects if you want.
This demonstrates the flexibility of Node, its callback model and the possibility to write higher-order functions to create your own APIs that suit your needs. I publish it in hope that it may be useful as an example to understand the Node callback style.
Because without this convention, developers would have to maintain different signatures and APIs, without knowing where to place the error in the arguments array.
In most cases, there can be many arguments, but only one error - and you know where to find it.
Joyent even wrote about this at the time they were more involved:
Callbacks are the most basic way of delivering an event
asynchronously. The user passes you a function (the callback), and you
invoke it sometime later when the asynchronous operation completes.
The usual pattern is that the callback is invoked as callback(err,
result), where only one of err and result is non-null, depending on
whether the operation succeeded or failed.
Yeah we can develop code style as you said. But there would be some problems.If we maintain code style what we want , different signatures of API increases and of course there would be dilemma between developers. They create their layers( error and success stages for example) again. Common conventions play an important role in spreading best practices among developers.
Ingenral, the error-first Node-style callbacks is what was originally used by Ryan Dahl and it is now pretty universal and expected for any asynchronous functions that take callbacks. Not that this convention is better than what you suggest or worse, but having a convention - whatever it is - make the composition of callbacks and callback taking functions possible, and modules like async rely on that.

Catch the Exception Running on different thread/callback

I have a Function executing Some piece of code Like,
Protected void XXXXfunc()
{
//i register a callback for asynchronous operation below is just an example
// not true to its operation
byte[] buffer = new byte[10];
s.BeginReceive(buffer, 0, 10, SocketFlags.None,
new AsyncCallback(OnMessageReceived), buffer);
}
// Callback function
XXXX callback OnMessageReceived(XXXX)
{
//Something Goes wrong here i throw an exception
throw(exception);
}
Where and how do i catch this exception or where is this exception funneled to be caught.
In the callback, the only place you can catch it.
And yes, that's a very awkward place because that callback runs on a thread you didn't start and runs completely asynchronously from the rest of your code. You have to somehow let the main logic in your program know that something went wrong and that corrective action needs to be taken. Which typically requires raising an event that gets marshaled back to your main thread. At the very least to let the user know that "it didn't work".
This kind of problem is the prime motivation behind the Task<> class in C# version 4 and the async/await keywords added to C# version 5. Which doesn't actually do anything to help the user deal with random failure, it just makes it easier to report.

Resources