Callback mechanism in Javascript - javascript

I am trying to understand the callback mechanism in javascript(typescript). If I have a function which expects a callback as input argument, do I have to explicitly use a return statement to hook it up with the actually callback implementation form the calling code OR we can simply have use "callback" reference in the code being called and it automatically hooks up with callback code in the calling code
Code samples (typecript)
// callback code being hooked up using return statement
clear(collectionName: string, callback: any) {
this.getConnection((err, db) => {
if (!db)
return callback(err, null);
db.collection(collectionName).remove();
});
return callback();
}
// callback code being hooked up using reference to reserved callback keyword for automatic hook up with calling code
clear(collectionName: string, callback: any) {
this.getConnection((err, db) => {
if (!db)
return callback(err, null);
db.collection(collectionName).remove({}, callback);
});
}

You don't need to return, you can simply call it like
clear(collectionName: string, callback: any) {
this.getConnection((err, db) => {
if (!db)
callback(err, null);
db.collection(collectionName).remove();
});
return callback();
}
// callback code being hooked up using reference to reserved callback keyword for automatic hook up with calling code
clear(collectionName: string, callback: any) {
this.getConnection((err, db) => {
if (!db)
callback(err, null);
db.collection(collectionName).remove({}, callback);
});
}

No. Functions are first-class citizens in Javascript thus you can use them as a normal variables and pass them all around. You just need to call them (as normal functions) at proper moments. No return statement required. For example look at how Array.prototype.forEach works under the hood. It accepts the callback as a parameter that gets currently iterated element as a first argument and current index as a second argument:
Array.prototype.forEach = function(callback) {
for(i = 0; i < this.length; i++) {
if (callback) callback(this[i], i)
}
}

Related

How do I use a callback in a nested function in Node?

I'm receiving a "TypeError: callback is not a function" which leads me to believe I am misunderstanding how to use callbacks in a nested function.
Function:
function getAudioInfo(filePath, callback) {
ffprobe(filePath, { path: ffprobeStatic.path }, function (err, info) {
console.log("ffprobe output: " + JSON.stringify(info));
if (err) {
console.log("getAudioInfo error: " + err);
callback(err, null);
} else {
callback(null, info);
}
});
}
Call:
function checkAudioInfo(metadata_json, callback) {
var filePath = metadata_json['current_path'];
getAudioInfo(filePath, function(err, info) {
if (err) {
callback(err);
}
//operations on info
callback(null, metadata_json);
});//end getAudioInfo
}//end checkAudioInfo
Is this an improper use of callbacks?
Edit:
Error was found in passing to the function wrapping checkAudioInfo (another callback error). I will make edits and post the correct code shortly. All your questions helped me figure out the answer. Thanks!
Your first block of code accepts a callback. The second argument should be a function. This function will be called when the asynchronous code is done.
Your second block of code calls the function in the first. The second argument you are passing is a function. So far, so good.
Inside that function, you try to call callback. This fails because there is no variable with that name in scope.
At this point, it is very unclear what you are trying to do. The function you are passing is the callback. You are supposed to use it to do something useful with the data it is passed by the code from the first code block.
Now, you could get a reference to the callback function by using a named function expression:
getAudioInfo(filePath, function callback (err, info) {
if (err) {
callback(err);
}
//operations on info
callback(null, metadata_json);
});//end getAudioInfo
… but then you are just calling it recursively and infinitely, which is not useful.
re Edit:
You have now added a second variable called callback:
function checkAudioInfo(metadata_json, callback) {
This makes more sense.
If that callback is undefined, then that is because you aren't passing it a value when you call checkAudioInfo. You haven't included that code.
The function itself function(err, info) is the callback which is called inside of getAudioInfo!
What is metadata_json? Do you mean info?

Need help understanding parameters [duplicate]

I have trouble understanding how callbacks in JavaScript get their arguments. Or in other words: how to implement a higher-order function so its callback accepts for instance a standard err and data arguments.
Like in this article on asynchronous JavaScript in the example of a callback used in a usual way (and I know it's usual because I see exactly this pattern used in Node.js moongose (i.e. for creating data in db)):
function getData(options, callback) {
$.get("example.php", options, function(response) {
callback(null, JSON.parse(response));
}, function() {
callback(new Error("AJAX request failed!"));
});
}
// usage
getData({name: "John"}, function(err, data) {
if(err) {
console.log("Error! " + err.toString())
} else {
console.log(data);
}
});
how exactly the callback gets arguments err & data based on how getData() function is declared above?
Arguments are passed to a function when that function is called.
function foo(arg) {
console.log(arg);
}
foo("This is the value");
This is still true when it is a callback function.
function foo(arg) {
console.log(arg);
}
function bar(callback) {
callback("This is the value");
}
bar(foo);
And it is still true when the callback function is called by code written by someone else that exists in a library you aren't examining the source code of.

Why does it matter to Mocha's `before()` whether the function passed takes parameters or not?

I have code like this in my describe: before(a).
When a looks like this:
function a() {
return chai.request(app)
...
.then(res => {
res.blah.should.blah;
return Promise.resolve();
});
}
...everything works great (it's a very quick call.)
However, when I make a take some input variables:
function a(dummy, my_var) {
return chai.request(app)
... // use my_var here
.then(res => {
res.blah.should.blah;
console.log("finished!");
return Promise.resolve();
});
}
The promise never resolves. Two observations:
finished! does get properly output (immediately)
dummy is populated.
It's populated with this:
function (err) {
if (err instanceof Error || toString.call(err) === '[object Error]') {
return done(err);
}
if (err) {
if (Object.prototype.toString.call(err) === '[object Object]') {
return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err)));
}
return done(new Error('done() invoked with non-Error: ' + err));
}
if (result && utils.isPromise(result)) {
return done(new Error('Resolution method is overspecified. Specify a callback *or* return a Promise; not both.'));
}
done();
}
I don't know where that's from exactly, but I don't need it, that's why I just put it into a dummy variable.
Now I suspect that has something to do with it, but I wonder why my workaround doesn't do the trick here.
I still want to use before(a) for the default case, leaving my_var undefined. When I do actually want to pass that variable, I intended to:
before(() => {
return a('my_content');
})
That's because Mocha inspects the function you pass to before to check how many parameters are defined on it like this:
this.async = fn && fn.length;
If there's any parameter defined, then the function is deemed to be asynchronous. (Mocha also checks if it returns a promise but that's a different test.) If there's at least one parameter defined, then Mocha passes as 1st parameters a function that you are meant to call when your before callback is done doing its work. That callback (traditionally named done) is useful for code that does not use promises.
If you do not call it, then Mocha waits forever even if you return a promise.
Note that Mocha does the same with all other hooks: beforeEach, after, etc., and with the callback you pass to it.
Mocha before, after, it function accepts one parameter - callback. It should be defined and called when your test uses async functions:
db.connect is async function:
before(done => {
db.connect(done);
}
When db.connect returns a promise you shouldn't use callback and could return the promise:
before(() => {
return db.connect();
}
In your code, you call before with a function, which accepts two parameters. Mocha interprets the first parameter as callback, and tries to call it.
To prevent this issue, you need to call a function directly and pass params in it:
before(() => {
return a(dummy, my_var);
}

Javascript: pass parameters to callback function

I want to call a function on the returned data from this database query, as in the snippet below.
function connectDB(query, data, callback) {
pg.connect(process.env.DATABASE_URL, function (err, client) {
if (err) {
console.log(err);
} else {
client
.query(query, data, function (err, result) {
callback(result.rows);
});
}
}
}
function foo(data) {
connectDB("SELECT * FROM mytable WHERE id=$1", someID, callbackfn(data));
}
I don't know how to write callbackfn so that I can use both data from the original function and rows from the db result.
function callbackfn(data) {
return function(rows) {
// Do something with rows and data
}
}
You don't accept any params with the callback function. How about:
function callback(rows) {
/* not sure what your intention was here? nested functions? */
return function () {
return function () {
// Do something with rows
}
}
}
And to be clear, can you post where you call the connectDB function? As callback is name of the variable after it is passed to connectDB.
Why are you defining callback function? Please elaborate on this. Also, try calling connectDB like this:
connectDB(query, data, function(rows) {
// Do something with rows
});
Read this answer on callbacks.
You have an incredible level of nested functions. Moreover, you are defining callback as parameter, but it won't take the callback function you defined, if you are not calling the base function passing again "callback" as argument.
If you want to pass always the same callback, it doesn't make sense that you provide it as argument, you can directly call "callback" inside your "connectDB" function. Moreover your callback returns a function, so it should be called again using again ().
Then your main callback function needs to accept rows as parameter from outside.
I kept your code using the callback parameter because I "hope" that the same name was just an example to explain that you want to provide every time a function in order to manipulate as you want the rows.
You code should look like this:
function connectDB(query, data, callback) {
pg.connect(process.env.DATABASE_URL, function (err, client) {
if (err) {
console.log(err);
} else {
client
.query(query, data, function (err, result) {
// this returns a function so it needs to be called again
callback(result.rows)();
});
}
}
}
// third inner function was useless
function callback(rows) {
return function () {
// Do something with rows
}
}
// the third parameter HAS TO BE callback, otherwise you will not pass the function you defined.
connectDB(query, data, callback);

Function not returning results

I'm trying to refactor some complexity into a function called getData but code that calls this function doesn't seem to be getting the results.
function getData(sql) {
pool.getConnection(function(err, connection) {
if (err) return;
connection.query(sql, function(err, rows) {
if (err) return;
if (rows.length > 0) {
console.log(rows); // This outputs result from table
return rows;
} else {
return [{"error":"Not found"}];
}
});
connection.end();
});
}
However, when it is called from a function like this, I get undefined returned, even though code inside the function works fine.
app.get('/1/employees/past', function(req, res, next) {
var rows = getData("select * from users");
res.json(rows);
})
Your return is returning from the inner function, which doesn't affect the outer function.
You'd need to capture it and return it, and seeing as it appears to work with callbacks, you'd need to pass an additional callback to getData().
Something like this...
function getData(sql, callback) {
// ...
connection.query(sql, function(err, rows) {
// ...
callback && callback(rows); // etc
});
// ...
}
If you wanted to be safer, ensure the callback implements [[Call]] with typeof (link to my own blog post).
Your getData function does not return anything - the return statements inside the code are all for the anonymous functions which you pass into connection.query and connection.query.
Your connection-related function are asynchronous - i.e. they return immediately after you call them without waiting for any results to be available. If you want to do something with the results which get returned you need to do it inside one of the anonymous functions, rather than trying to do it as soon as getData completes.
The getData function is not returning anything! The getConnection function inside the getData function is calling a function which calls a function which returns something; the getData function itself is not returning anything.
What's more, if this code executes asynchronously, all the returns will execute long after the getData function has returned.
You're either going the synchronous return route or the asynchronous callback route, you can't mix both styles.

Categories

Resources