I have an aysnc function that takes an options object and callback
function myFunc(options,callback) {
// do stuff
callback(null);
}
being the defensive-minded guy that I am, I want to check for any potential problems in the parameters
so the first thing I did was to add a test for the options.name (because I need that property)
function myFunc(options,callback) {
if (!options.name) { callback(new Error("name must be specified")) }
// do stuff
callback(null);
}
but - this led to a train of thoughts .. what if
options are an empty or null object
options where not specified, just a callback passed
what if no parameters were passed at all ?
I want to check that my thoughts are right here -
options is null / empty. callback is defined -> callback with error
options is undefined. There are no options and no callbacks -> throw a new error
options is defined, callback is not
typeof options is a function -> use options as the callback, with an error
typeof options is not a function -> throw an error
so, that's a lot of checks ... do people really do all of this ? What is the best practice to ensure parameters are present and correct ?
Since this question has a sense of personal touch, here's mine:
The purpose of a callback is to provide results of an asynchronous operation. As such, it only needs to be called asynchronously. If anything is prohibiting your code from completing its task, and you can identify that synchronously, either return an error value if one is defined or throw.
If the asynchronous operation failed, use the callback to notify.
Side note options can be undefined AND callback can be a valid function at the same time. Don't assume otherwise.
var func = function(p){
if (p == null){
alert('passed null');
}
if (typeof p === 'undefined'){
alert('not passed at all')
}
you can check it here: https://jsfiddle.net/1s1nqdc7/
Related
I am new learning to create rest API's using Node, Express and MySQL. I have my function removeAll that takes no parameters, it passes the result object into an arrow function which then runs a sql function which then takes another function as a parameter (err, res). Inside of the nested arrow function if we do not have any errors and the affected rows does not equal 0 we get to the result which returns (null, res). Now my confusion is as to why result contains (null, res) rather than just res. I am also confused as to how res is actually being assigned, I see that it is being passed into the arrow function but we dont explicitly set what res is.
sql.query("DELETE FROM customers", (err, res) => {
if (err) {
console.log("error: ", err);
result(null, err);
return;
}
if (res.affectedRows == 0) {
// customer not found with the id
result({ kind: "not_found" }, null);
return;
}
console.log("deleted customer with id: ", id) {
result(null, res);
}
});
};
result is a node-style callback, where the first parameter is the error (if any; if no error, first parameter is null), and the second parameter is the result (if no error is encountered).
The general approach is very similar to the sql.query callback you're using:
sql.query("DELETE FROM customers", (err, res) => {
Just like how its callback defines the error parameter first, and the result second, you similarly want to call result with the error as the first parameter, or else with the result as the second parameter.
That said, result is a pretty confusing variable name - it's a callback, not a result which contains data, so you might want to rename it to something more appropriate, so as to reduce potential confusion.
I am also confused as to how res is actually being assigned
It's handled by the internals of sql.query. When the query resolves successfully, it will do something like callback(null, results). When the query fails, it will do something like callback(someError). callback is the callback that you pass to sql.query.
Now my confusion is as to why result contains (null, res) rather than just res.
That's the standard way old-style Node.js callbacks work: The first argument is an error or null, and if the first argument isn't an error, the second argument contains the data "returned" by the call.
These days, you'd use a promise, but that's how the old-style callbacks work.
I am also confused as to how res is actually being assigned, I see that it is being passed into the arrow function but we dont explicitly set what res is.
The sql.query function is what calls the callback you pass into it. That's where res comes from, it's the result of the database operation. sql.query also follows the standard old-style Node.js callback pattern.
Task: I need to recursively walk through a json object and make certain changes to the keys. I will be handling objects with varying depths, and varying sizes. When the function hits a key whose value is an object, it is called again on that object.
Problem 1.: Doing this as a synchronous function, I noticed that large json objects were returning incomplete. Using the the async library, async.forEach solves the problem of handling long tasks and returning only when finished, but...
Problem 2.: It seems that the async function loses concurrency (?) when it is called recursively (pointed out in the code snippet).
To test this idea, I removed the recursive function call and it worked (but without recursion), and I got a callback. When I add the function call back in, I get TypeError: results is not a function .
This leads me to think that async with callback and recursive functions don't mix in node. Is there a way to achieve both?
Possible Fix: I could run a separate function to count all the keys and use a counter as my control in a simple for loop, rather than letting forEach handle the control. That seems a bit inefficient, no?
Here's the code:
function fixJsonKeys(obj, results) {
async.forEach(Object.keys(obj), function(key, callback) {
if (typeof obj[key] == 'object') {
// do stuff to json key, then call the function
// on the nested object
fixJsonKeys(obj[key]);
callback(); // <-- how does this work with recursion??
}
else {
// do stuff to json key
callback();
}
}, function(err) {
if (err) return next(err);
// obj keys fixed, now return completed object
results(obj);
});
}
EDIT: hard to format in comments, so:
#Laksh and #Suhail: I tried your suggestions and same outcomes. If I remove the callback from the if condition, the async.forEach still looks to confirm that it has handled the top level keys (I think).
For example, say I have 3 top level keys, and one of them has a nested object:
[key1] (no callback, do recursion)
--[subKey1]
--[subKey2]
[key2] (callback)
[key3] (callback)
async.forEach is still looking for a callback on action taken for key1. Do I have this correct?
Since you are using recursive functions here, you don't need to call callback() inside the if condition.
This is because your recursive call will return to the existing callback() in if once the entire recursive stack finished for that particular obj[key].
Recursive function return only when the base condition is true, in your case when if condition fails so it will automatically call callback() from else block.
I am very confused when it comes to javascript callbacks... Especially when attempting to learn node at the same time.
You see, I understand the concept of passing a function as a parameter to another function but what confuses me mostly are the premade modules that "accept" callbacks functions.
For example a simple request looks like so:
var request = require('request');
var url = 'http://www.google.com';
request.get(url, function(error, response, body) {
if(error){
console.log('This request resulted in an error', error);
}
console.log(body);
});
This get method accepts a url and a callback.
Who defines that a callback is accepted? The developer of the specific method?
Also, the 3 parameters that we pass within the function (error, response, and body)... how does the request module know to populate these variables with data on return?
Imagine get implemented more or less like
function get(url, callback) {
var response = they somehow retrieve the response;
var body = they somehow parse the body;
var error = ...;
// they call you back using your callback
callback( error, response, body );
}
How these values are computed is irrelevant (this is their actual job) but then they just call you back assuming your function actually accepts three arguments in given order (if not you just get a runtime error).
In this sense they also kind of "accept" your callback by ... well, calling it back :) There is no worry about the number of arguments as Javascript is very "forgiving" - methods can be called with any number of arguments, including less or more arguments than the function expects.
Take this as an example:
// they provide 3 arguments
function get( callback ) {
callback( 0, 1, 2 );
}
// but you seem to expect none
get( function() {
});
The get assumes that the callback accepts three arguments while I pass a method that supposedly doesn't accept less arguments. This works, although the three arguments, 0, 1 and 2 are not bound to any formal callback arguments (you can't directly refer to them), they are there in the arguments object, an array-like structure:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
Take another example
function get( callback ) {
callback( 0, 1, 2 );
}
get( function(a,b,c,d,e) {
});
This time the callback I pass seems to expect more arguments than the caller provides and still, no problem here. Arguments are bound to consecutive call parameters, a will get 0, b will get 1, c will get 2 and d and e will get undefined inside the callback body.
Both examples show that the caller doesn't really care how your callback is defined, whether its number of arguments matches the expectation or not. In case of any mismatch, the worst what could happen is a runtime error:
function get( callback ) {
callback( 0, 1, 2 );
}
get( 1 );
// Uncaught TypeError: callback is not a function
The designer of a function/method decides what arguments it will accept and process. So, if a function accepts a callback as an argument, then it is the design and implementation of the function that decided that and will call the callback at the appropriate time.
It is also the implementer of the function/method that decides what arguments will be passed to the callback when it is called. What confuses many people is that is is the caller of the function that defines the callback function itself that will be called by the main function/method. But the arguments of the callback function MUST be declared to match what the main function/method will pass to the callback. The labels given to the arguments when you define the callback are merely labels for programming convenience (as a means of referring to the arguments by name). How the callback's arguments are defined in the callback function has nothing to do with what is actually passed to the callback - that is all decided by the function/method that calls the callback. So, when you use request.get(), the contract for using it is that it accepts a callback that should be declared to have the arguments (error, response, body) because that's what it will be called with. Now, it is OK to leave off arguments at the end if you aren't going to use them (they will still be there, but you just won't have a convenient name to refer to them by), but you can't leave off arguments before any that you intend to use because order is important.
Who defines that a callback is accepted? The developer of the specific
method?
Yes, the developer of the function/method decides if a callback is accepted.
Also, the 3 parameters that we pass within the function (error,
response, and body)... how does the request module know to populate
these variables with data on return?
The request.get() method is programmed to pass three arguments (error, response, body) to its callback and that's what it will pass. No matter how the callback itself is actually declared, request.get() will pass these three arguments in that order. So, what confuses many people is that the caller of the function/method creates the callback function itself and gets to define the arguments for it. Well, not really. The creator of the callback must create a callback that matches the contract the the function/method is expecting. If it doesn't match the contract, then the callback likely won't function properly as it will get confused about what it was passed. Really the contract here is that the first argument to the callback will be an error value and if that value is not truthy, then the next two arguments will be response and body. Your callback must match that contact in order to use those arguments correctly. Because Javascript is not strictly typed, it will not be a run-time error if you declare your callback incorrectly, but your access to those arguments will likely be wrong - causing things to work incorrectly.
For example, if you declared your callback to be:
function myCallback(response, body, err) { .... }
Then, what your callback sees as response would actually be the error value because request.get() put the error value in the first argument, no matter how the callback itself is declared.
Another ways of seeing this is to define a callback with no defined parameters and then access them entirely with the arguments object.
function myCallback() {
console.log(arguments[0]); // err value
console.log(arguments[1]); // response value
console.log(arguments[2]); // body value
}
The same is true of the return value in the callback. If you are using Array.prototye.map(), it expects a callback that is passed three arguments and returns a new array element. Even though you are implementing the callback, you must define a callback that is compatible with that contract.
This get method accepts a url and a callback. Who defines that a callback is accepted? The developer of the specific method?
Yes. The author(s) of request.get() defined it specifically to expect and make use a "callback" function.
You can define your own functions to accept callbacks as well, especially useful when they depend on other asynchronous functions – such as setTimeout() for a simple example.
function delay(value, callback) {
setTimeout(function () {
// this statement defines the arguments given to the callback
// it specifies that only one is given, which is the `value`
callback(value);
}, 1000);
}
delay('foo', function (value) { // named to match the argument `delay` provides
console.log(value);
});
Also, the 3 parameters that we pass within the function (error, response, and body)... how does the request module know to populate these variables with data on return?
The arguments given and their order are part of request's definition, same as delay() above defining that (value) would be the argument(s) for its callback.
When providing a callback, the parameters only give the arguments names for you own use. They have no effect on how the main function (request.get(), delay(), etc.) behaves.
Even without naming any parameters, request.get() is still passing the same 3 arguments in the same order to the callback:
request.get(url, function () { // no named parameters
var error = arguments[0]; // still the 1st argument at `0` index
var body = arguments[2]; // still the 3rd argument at `2` index
if(error){
console.log('This request resulted in an error', error);
}
console.log(body);
});
It is a convention in node to pass an error parameter to asynchronous operations:
async.someMagicalDust(function(callback) {
// some asynchronous task
// […]
callback();
}, function(err) {
// final callback
if(err) throw err;
// […]
});
Maybe I'm too naive, but I've never been a big fan of the if(variable) notation — probably inherited from C, for reasons that have already been discussed many times in the past.
On the other hand, I have sometimes encountered a null parameter and this error check:
if(typeof err !== 'undefined' && err !== null)
is a bit too verbose.
Another solution would be
if(err != null)
but I think the non-strict check can be tricky, even though I consider it normal when comparing with null.
What's the best way to check error parameters in node?
Use if(err).
It is designed to be used in this fashion. Node-style callbacks are supposed to set the error to a non-falsy value only in case of actual error. You won't find any sane example of setting err to '' or 0 to signify error.
Just like YlinaGreed has noted, if module conventions cnahge from null to undefined to 0 to maybe even NaN, you are still safe. I've never been hit by this having only used if(err).
On the other hand, you may want to use coffescript, which would translate for you
unless err? then...
into
if (typeof err === "undefined" || err === null) {
mimicking the most common pattern.
Some links to corroborate the if(err) approach:
https://gist.github.com/klovadis/2548942
http://caolanmcmahon.com/posts/nodejs_style_and_structure/ (by Caolan McMahon, author of async)
http://thenodeway.io/posts/understanding-error-first-callbacks/
The convention seems to be passing an error object as the first argument and null for no error so even if you pass an empty object, it's still an error.
If you use the popular express framework, you should have used the next callback to return from middleware, which follows the errback convention.
I believe that most people prefer more concise next() than next(null), which means that the first argument will evaluate to undefined rather than null, and this is certainly perfectly normal usage.
To me, the best way to handle error is the "if (err == null)"
This is the only case I am using the non strict operator, for these reasons:
The only over solution which always works is very verbose, as you said before
You could also check only on "null" or "undefined", but I did this once, and a few month later, I just updated my dependencies and... The convention changed, and the module was sending null instead of undefined.
This is mostly a matters of "convention", I have mine, and you certairnly have your's too... Just be careful to chose one of the two "good" ways.
Node's primary callback convention is to pass a function with err as the first parameter. In my experience its always been safe to check if error is truthy - in practice if your error comes out null when there IS an error then the problem lies more with the implementation. I would always expect if err is null that no error occured. It may be confusing due to the use of separate functions for errors and successes, something that is more in the style of JQuery.Ajax and promises. I tend to find double callbacks to be a bit too wordy to call.
Given your example it seems you're using the async library which is excellent. If I am looking to perform a parallel option this is how I set it up:
function doAThing(callback) {
var err;
// do stuff here, maybe fill the err var
callback(err);
}
function doAsyncThings(callback) {
var tasks = [function(done) { // stuff to do in async
doAThing(function(err) {
done(err);
});
}];
async.parallel(tasks, function(err) { // single callback function
callback(err); // I send the error back up
});
}
Note that instead of throwing the error I bubbled it back up the request chain. There are few instances which I'd want to actually throw an error since you're basically saying "crash the whole app".
I find it to be simpler and reduces the amount of parameters you have to use to call your functions. When you use this convention throughout you you can simplify by simply passing the callback as a parameter instead of creating a new anonymous function, like so:
function doAThing(callback) {
var err;
// do stuff here, maybe fill the err var
callback(err);
}
function doAsyncThings(callback) {
var tasks = [function(done) { // stuff to do in async
doAThing(done);
}];
async.parallel(tasks, callback); // the error is sent back to the original function
}
I find that generally you want to handle those errors in the functions they are called in. So in this case the caller of doAsyncThings can check if there is an error and handle it appropriate to its own scope (and perhaps provide better information to the user if it is say an API).
I was reading this article on howtonode,
but don't get why it's fake async ?
So fake async is described as:
function asyncFake(data, callback) {
if(data === 'foo') callback(true);
else callback(false);
}
asyncFake('bar', function(result) {
// this callback is actually called synchronously!
});
Correct code: always async
function asyncReal(data, callback) {
process.nextTick(function() {
callback(data === 'foo');
});
}
My question is what's wrong with the first part of code ?
Why nextTick() can promise the 'right' effect ?...
Please explain to me. Thanks.
Nothing's wrong with the first case. However, it's just that you have to be proactive (as the developer) to know exactly what's happening in your code.
In the first case, you are not doing any I/O, and the callback is not actually being put into the Event Loop. It just the same as doing this:
if(data === 'foo')
return true;
else
return false;
However, in second case, you are putting the callback into the event loop until the next iteration.
In terms of how things work, nothing's wrong. However, you need to be aware of what implications are.
For instance:
function maybeSync(a, cb) {
if(a === 'a') {
cb('maybeSync called');
} else {
// put the called into event-loop for the next iteration
process.nextTick(function() {
cb('maybeSync called');
});
}
}
function definitelySync() {
console.log('definitelySync called');
}
someAsync('a', function(out) {
console.log(out);
});
definitelySync();
In the top case, which one gets called first?
If the input is "a" then the output is:
maybeSync called
definitelySync called
If the input is something else (e.g. not "a") then the output would be:
definitelySync called
maybeSync called
You need to make them consistent, as it would be easy to distinguish if the callback can be called sync/async based on the condition. Again comes back to being responsible, being consistent and being aware of what's happening in your code. =)
In the async fake function - you can see that callback is called as a normal function invocation i.e in the same call stack.
someone calls async fake fn
async fake fn calls the callback passed to it
If that callback takes time to complete - the async fake fn is kept waiting i.e it remains in call stack.
By using process tick - we are just submitting the callback function to be scheduled for execution.
This process tick call completes immediately and the asyncreal will return immediately. Thus the callback will be executed in asynchronous way.