In node.js asynchronous functions have callback, however only some of them have err argument passed to that function.
e.g. fs.writeFile has err as parameter
fs.writeFile('message.txt', 'Hello Node', function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
but fs.watchfile doesn't
fs.watchFile('message.text', function (curr, prev) {
console.log('the current mtime is: ' + curr.mtime);
console.log('the previous mtime was: ' + prev.mtime);
});
First question is why some async functions have err argument in callback and some don't?
How are we supposed to handle error of those which don't?
Also, as far as synchronous functions are concerned, are they all emitting "error" event which we can subscribe to and handle error this way?
var rs = fs.createReadStream("C:\\Temp\\movie.mp4");
rs.on('error', function(err) {
console.log('!error: ', err);
});
and last question: most of synchronous functions have Sync in name... why createReadStream does not?
Thanks!
First question is why some async functions have err argument in callback and some don't? How are we supposed to handle error of those which don't?
The vast majority of asynchronous functions in node code conform to the convention of the first argument to the callback function is the error. There are a very small number of exceptions, such as fs.exists, which is clearly documented as an antipattern that should not be used in the official docs.
In the case of watchFile in particular, it's just the semantics of the API that it calls the callback repeatedly and only on success just because that's what it means to watch a file and in general the OSes don't provide a mechanism that has the semantic "notify me when anything goes wrong with this filesystem path", so there you have it.
Also, as far as synchronous functions are concerned, are they all emitting "error" event which we can subscribe to and handle error this way?
No. Your categorization of node mechanics is incomplete. There are at least 4 major paradigms:
synchronous code. Mostly throws exceptions to indicate errors like JSON.parse but not always. For example, parseInt returns NaN to indicate an error.
asynchronous code in callback style. Generally passes an error as the first argument to the callback.
asynchronous code in the event emitter/streaming style. Generally emits an "error" event.
asynchronous code using promises. Not used in node core but has a large and loyal set of devotees in the community. Generally rejects the promise via invoking the "reject" callback which is the second argument passed to .then.
most of synchronous functions have Sync in name... why createReadStream does not?
Because it's not really synchronous. It's event emitter based streaming. The actual createReadStream will synchronously return you the stream object, but the I/O is asynchronous and events won't start to be emitted until the next tick at the earliest.
I highly recommend the article Error Handling in Node.js on the joyent blog for the current best practices described thoroughly.
Related
The specific function that I want to catch errors is from the Node.JS HTTP module:
response.write(data);
This function does not return a promise and is asynchronous so putting it in a try/catch doesn't work either. It does take an optional callback argument but this is only called on success of the operation. I also can't use await as this is function is not defined as an async function.
What is the best method for handling errors thrown by this function?
From the documentation:
The write() method […] calls the supplied callback once the data has
been fully handled. If an error occurs, the callback may or may not be
called with the error as its first argument. To reliably detect write
errors, add a listener for the 'error' event.
For example I just tried to catch ENOENT by naively doing this:
try {
res.sendFile(path);
} catch (e) {
if (e.code === 'ENOENT') {
res.send('placeholder');
} else { throw e; }
}
This doesn't work!
I know that the right way is to use the error callback of sendFile, but it's really surprising and bad for me that the fundamental language feature of exceptions doesn't work here.
I guess maybe express itself is doing the catching. And they want to not have the errors kill the server so quickly. It's understandable.
But I just get this lame message:
Error: ENOENT: no such file or directory, stat '<file>'
at Error (native)
Not great.
Due to documentation res.sendFile is async function, so try/catch won't work in this case. If you want to handle res.sendFile result you must pass callback as last argument.
res.sendFile(path, function (e) {
if (e) {
if (e.code === 'ENOENT') {
res.send('placeholder');
} else {
throw e;
}
}
});
Its because of the Asynchronous nature of Javascript that the code will not catch the exception you are throwing. The res.sendFile is executed outside the scope of try block and the try block execution will be over after the call to res.sendFile method.
That is the reason why it is always advised to use the callback mechanism with err object as the first argument to the callback and you can check that first before proceeding
res.sendFile(path, function (e) {
// check the error first
// then procedd with the execution
});
Generally speaking, you want to avoid using exceptions to control program flow.
Also, since you're apparently programming this in Node.js, you pass a callback function to keep Node running asynchronously. Node.js is not multithreaded, because JavaScript is not multithreaded, so it cannot have more than one thing going on at a time. That means that if your code is hung up handling an exception, nothing else is happening. And Exceptions are expensive. Doing expensive cleanup in a single-threaded server-side application will harm performance and scalability. Nor is JavaScript itself magically asynchronous. It's asynchronous only if you use callback functions to loosen it up.
So when you pass a callback function to res.send, that function gets called asynchronously when the res.send function finishes (or exits without finishing due to an error), without the overhead of a thrown exception. Thus, if you want to handle errors, passing a callback method is the way to do it.
I have a nodejs express app and I use a library which has a typical callback interface for executing functions. My persistence layer uses a promise based approach. I have the following code which bothers me
getUserByName('dave')
.then(function (user) {
// check stuff and call the callback with success
return cb(null, true);
})
.catch((err) => cb(err, false));
Problem: the cb(null, true) functions returns undefined and the promise ends with this warning a promise was created in a handler but was not returned from it.
I can fix this by running the callback and then do return null like this:
// check stuff and call the callback with success
cb(null, true);
return null;
But now I'm asking myself is it really waiting for the callback to finish? Is this the correct way to handle this kind of warning? I have the feeling I'm doing it wrong.
I remember having the same problem when writing an express middleware and then inside a promise calling the next() function to jump to the next middleware. It also returns undefined. Any suggestions to handle this?
Well, the proper solution would of course be to switch to a framework that doesn't use node-style callbacks and leverages promises, so that you could simply return your promise and wouldn't need to call any callbacks passed to you.
If that is not possible, you still should not call such callbacks from your normal code. The warning is correct, you are calling something (the callback) that does more asynchronous work (creates other promises) but are not returning that into your chain ("forgetting" to wait for it), a common mistake. Your explicit return null suppresses this warning correctly, but there's actually a much better way:
Write your code as if you already were returning promises, and then invoke .asCallback which is dedicated for this very purpose (including not issuing warnings):
getUserByName('dave')
.then(function (user) {
// check stuff and call the callback with success
return true;
})
.asCallback(cb)
But now I'm asking myself is it really waiting for the callback to
finish? Is this the correct way to handle this kind of warning? I have
the feeling I'm doing it wrong.
The Javascript execution in node.js is single threaded so the code is waiting for any synchronous code in cb() to finish. If cb() does async operations (which the warning you are getting makes me think it is), then your code is NOT waiting for those asynchronous operations to finish.
Whether or not your work-around is the correct way to handle that warning depends upon what is happening in the callback and whether your code needs to wait for any async operations in that callback to actually complete. If your code does not need to wait for them, then adding the empty return is perfectly fine and just tells the promise library that you are purposely not returning a promise that was created inside the .then() handler which is an OK thing to do in some circumstances.
If, on the other hand, you do need to wait for the asynchronous operations inside of the callback to complete, then you need the callback's help in order to be able to do that by making it either return a promise or for it to have a completion callback itself (though the promise would be a lot easier in this case).
Hopefully this is not too abstract but I am looking for advice on expected behavior when creating a Node module API. I am finding that in my module implementation, checking to make sure the caller has provided a callback before I call it is getting messy. I am starting to think its user error to not provide a callback for an API that clearly needs one to do its job.
Being a Node beginner, what is the general pattern for modules? I took a look at the Node source code itself. It seems like in some places they use a function that swaps an unsupplied callback with a generic one that throws an error. This seems like it might lead to confusion though.
Thanks
You can provide a default callback. Depending of your needs and the importance of the callback, you can provide a noop function or just a function that only check if there was any error.
if (!callback) {
callback = function noop() {}
}
Throwing or showing the error you'll decide. (Better to not throw most cases)
if (!callback) {
callback = function handleError(e) {
if (e) console.log(e);
// Or
if (e) throw(new Error(e))
}
}
I have a basic understanding of javascript and have been learning how asynchronous functions work in node.js. I've been very confused by callback functions with the parameter error. For example, here's some code:
contact.saveContacts = function(contactArray, done) {
var jsonfile = require('jsonfile')
jsonfile.writeFile('data.json', contactArray, done)
}
Contact.saveContacts(contacts, function(err) {
console.log('success')
}
My question is, why does the callback function contain the parameter error? I'm confused to why it's there because it seems as if it serves no purpose in the function it calls.
This is a pattern called an error first callback and is used a lot in javascript.
See this article for reference.
Typically synchronous functions either return successfully, possibly with a value, or throw an exception if there is a problem. The calling code can choose what to do if an exception is thrown, by either catching and inspecting the error or by letting it fall through to other code that may handle the error.
Asynchronous callback functions are called after the calling code has already executed. This means there's no opportunity to catch thrown exceptions. So instead of throwing, errors are passed through to the callback function so the calling code can handle both success and error states.
in case there is a problem with write operation such as permissions error object is invoked and the reason in this to prevent unexpected errors.
Imagine we are giving a order to do computer starts doing it but on the way theres a block about permissions that computer cannot write into that dir in this case computer doesnt know what to do and our program crashes to prevent this inside callback we specify what to do in such cases for example if permission is denied and the reason is write permission prompt user for password and force writing or open a box to user that user must run this as user
The error parameter has no usage if everything goes fine. But, its too useful when there comes an error. Any type of error e.g. runtime error, of file has been deleted or anything, if happens, the details of the error will be present in the error parameter of the callback. So, its better to use that parameter as following:
Contact.saveContacts(contacts, function(err) {
if(err){
console.log(err);
}
else{
console.log('success');
}
}
In this way, you will get to know any error, if that happens with the function.