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))
}
}
Related
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'm reading over a legacy codebase and I ran into this following code:
andThenWe: function(callback) {
var qunitAssertAsync = new window.AssertAsync(callback);
return qunitAssertAsync;
},
and here's the call site:
andThenWe(function(done) {
...(some code)
done();
});
So in the call site, we're passing in an anonymous function which will then be === 'callback' right? However, this callback has an argument called done and seems to be called at the end of this function. That argument is kind of like a block parameter in Ruby right? So somewhere in the window.assertAsync the callback MUST be called and passed some kind of arugment which is probably === to Qunit's assert.async right? (most likely). The details of the window.assertAsync are really complicated so I just want to understand at a high level what must be going on. Am I making proper assumptions?
This is all possible because callback in the function signature is an anonymous function that be invoked at a later time right? Also done itself in the callback function must be a function itself at runtime right?
I think this is an attempt to make qunit.async more "readable" (haha).
qunit.async is used to force tests to wait until an async operation has completed before exiting the test.
The done callback must be invoked when the writer of the test knows everything async has completed.
Newbie to Node.js here. I'm learning Node.js via the tutorials at NodeSchool.io, and in one tutorial where we learned about modules, we were required to write something like this code:
// Some code...
function filteredLs(dir, ext, callback) {
fs.readdir(dir, function(err, files) {
if (err)
return callback(err); // return statement necessary here...
callback(null, withExtension(files, ext)); // ...but not here
})
}
module.exports = filteredLs;
My question is, in examples like these, why is it necessary to include the return statement when handling the error, but OK to omit when it's null? I don't see what use the return value of the function could have to readdir anyhow, since it happens after it finishes its work. Why does it make a difference?
The use of return when calling a callback function is typically there to prevent the code that follows from running. The returned value is typically irrelevant.
That's why it's needed in the error case, so the callback call for the non-error case isn't also called.
It's not needed in the non-error case because it's already the last line of the function.
The error handling boilerplate you posted is indeed confusing. It uses a maximally-terse way of expressing the code, but is indeed confusing, and you are right that the return value is discarded. Thus my preferred boilerplate for clarity is
if (error) {
callback(error)
return
}
Which I feel is slightly clearer and the reduced concision is not that important to me (I type it with a macro anway).
I find this to make it clearer that there are 2 distinct intentions being expressed here:
Bubble the error back up to the caller
Exit the function as there's nothing else useful to be done. No return value because the callback protocol does not require one and the calling code does not need to and probably will not even capture the return value into a variable.
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.
The documentation at https://github.com/pivotal/jasmine/wiki/Matchers includes the following:
expect(function(){fn();}).toThrow(e);
As discussed in this question, the following does not work, because we want to pass a function object to expect rather than the result of calling fn():
expect(fn()).toThrow(e);
Does the following work?
expect(fn).toThrow(e);
If I've defined an object thing with a method doIt, does the following work?
expect(thing.doIt).toThrow(e);
(If so, is there a way to pass arguments to the doIt method?)
Empirically the answer seems to be yes, but I don't trust my understanding of JavaScript scoping quite enough to be sure.
We can do away with the anonymous function wrapper by using Function.bind, which was introduced in ECMAScript 5. This works in the latest versions of browsers, and you can patch older browsers by defining the function yourself. An example definition is given at the Mozilla Developer Network.
Here's an example of how bind can be used with Jasmine.
describe('using bind with jasmine', function() {
var f = function(x) {
if(x === 2) {
throw new Error();
}
}
it('lets us avoid using an anonymous function', function() {
expect(f.bind(null, 2)).toThrow();
});
});
The first argument provided to bind is used as the this variable when f is called. Any additional arguments are passed to f when it is invoked. Here 2 is being passed as its first and only argument.
Let’s take a look at the Jasmine source code:
try {
this.actual();
} catch (e) {
exception = e;
}
if (exception) {
result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
}
This is the core part of the toThrow method. So all the method does is to execute the method you want to expect and check if a exception was thrown.
So in your examples, fn or thing.doIt will be called in the Jasmine will check if an error was thrown and if the type of this error is the one you passed into toThrow .
If it is used like this:
expect(myTestFunction(arg)).toThrowAnyError(); // incorrect
Then the function myTestFunction(arg) is executed before expect(...) and throw an exception before Jasmine has any chance of doing anything and it crashes the test resulting in an automatic failure.
If the function myTestFunction(arg) is not throwing anything (i.e. the code is not working as expected), then Jasmine would only get the result of the function and check that for errors - which is incorrect.
To alleviate this, the code that is expected to throw an error is supposed to be wrapped in a function. This is passed to Jasmine which will execute it and check for the expected exception.
expect(() => myTestFunction(arg)).toThrowAnyError(); // correct
Sadly, it seems that if I need to test a function that takes parameters, then I will need to wrap it with a function.
I would rather have preferred,
expect(myTestFunction, arg1, arg2).toThrow();
But I am ok with explicitly doing
expect(function(){myTestFunction(arg1, arg2);}).toThrow("some error");
FYI, note that we can also use a regex match on error:
expect(function (){myTestFunction(arg1, arg2);}).toThrowError(/err/);