Yes I am a terminator fan...haha
ok coming down to nerdy talks.
Firstly from the name "Callback methods" itself, it is as if saying:
"I am going somewhere else now to do something else entirely different but a neccessity to complete this task, and I will be back with results and use it to resume and complete the flow ahead."
Here the neccessity is to interact with port 27017 and get a connection object from the mongodb service running at that port.
The I
here is the main execution thread, as seen in the below code, when the .connect is encountered, it signifies that you are trying to do something out of the bounds of your application logic, or you are trying to gain access to a resource which can be given to you by the OS or something else beyond the control of your application.
In this case you are trying to gain access to some service running @ 27017.
mongodb.connect("mongodb://localhost:27017", function(error, connection) {
//something block of code.
})
As you don't actually know when this task will be done, you supply the method with a call back function, which means once you get the connection come back here and execute this block of code.
mongodb.connect("mongodb://localhost:27017", function(error, connection) {
//handle error
//handle connection
crudHandler(connection, crudParams)
})
The mechanism of callback comes from the fact that, in Javascript a function can accept another function as an argument. Higher order functions.
Which also means that any running function can call another function either supplied as an arguement or kept inside a json and json is given in the argument, like in our case:
function crudOperations(crudHandler, crudParams) {
mongodb.connect("mongodb://localhost:27017", function(error, connection) {
crudHandler(connection, crudParams)
})
}
Here the running function is crudOperations which takes 2 args: crudHandler and crudParams crudHandler actually points to some function which will execute "as a function but placed inside a callback function",during actual code execution. crudParams this is our json object which has some functions hidden inside , like this:
function crudOperations(crudHandler, crudParams) {
mongodb.connect("mongodb://localhost:27017", function(error, connection) {
if(error)
crudParams.errorCallback(error);//we can supply an error callback, just to handle errors
else
crudHandler(connection, crudParams)
})
}
Here above the .errorCallback will run as function but placed inside the callback function.
Please note all the code above is like a plan , which will work only if the supplied argument (createParams)
contains the mentioned keys, will elaborate this below.
Moving on:
class Database {
constructor() {
this.create = function(createParams) {
crudOperations(createHandler, createParams)
}
function crudOperations(crudHandler, crudParams) {
mongodb.connect("mongodb://localhost:27017", function(error, connection) {
if(error)
crudParams.errorCallback(error);//we can supply an error callback, just to handle errors
else
crudHandler(connection, crudParams)
})
}
function createHandler(connection, createParams) {
const db = connection.db(dbProperties.databaseName);
const collection = db.collection(createParams.collectionName);
collection.insert(createParams.payload, function(error, rowsAffected) {
connection.close();
if (error)
createParams.errorCallback(error);
else
createParams.successCallback(rowsAffected)
})
}
}/* other methods */
}
module.exports = Database;
As you can see above this
points to Database
instance,create
is bound to this
that means when an external party creates a instance of Database
they can call create method using the (.) dot operator.
The arguement needed for the method is one json object which needs to have the following json schema / plan:
Please note unless the schema of the object passed to the instance of database, is not like this, it will not work as coded.
//JSON object
{
"collectionName": "FirstTable", //name of the collection
"payload" : {name:"John Doe"}, //some json data object that needs to be inserted to the collection mentioned above.
"successCallback": function(data) {
res.status(200).send(data);
},//this function will be called if everything goes well.
"errorCallback" : function(error){
res.status(500).send(error)
}//this function will be called if anything fails.
};
Here what we do is we weave the available rules and mould a logic that favors the flow that we want. Rules:
- Connect method needs a callback, it will perform the task of getting the connection and give us back two objects in the callback error and connection object.
- Here the point to our advantage is we can use this place as a placeholder and plug in the logic we want.
- A function can be passed to another function as an arguement.
Below is the user of Database.js
class HelloRouteHandler {
static handle() {
router.post("/", function(req, res) {
const database = new Database();
const insertParams = {
"collectionName": "FirstTable",
"payload" : {name:"John Doe"},
"successCallback": function(data) {
res.status(200).send(data);
},
"errorCallback" : function(error){
res.status(500).send(error)
}
};
database.create(insertParams)
});
return router;
}
}
module.exports = HelloRouteHandler
As you can see HelloRouteHandler.js
is:
const database = new Database();
making a new instance ofDatabase.js
const insertParams = { "collectionName": "FirstTable", "payload" : {name:"John Doe"}, "successCallback": function(data) { res.status(200).send(data); }, "errorCallback" : function(error){ res.status(500).send(error) } };
making the object which will be later passed to the create method as an arguement.database.create(insertParams)
finally calling the create method.
Here the only weird thing is the code hopping :(
- Only
HelloRouteHandler.js
has visibility toreq
andres
and we need to send something back to user, whether the operation he requested was successfull200
or did it fail500
or400
etc.HTTP Status Codes - The only ticket to send something back is by using the
res
object. - Hence we need to think-ahead in future,and code accordingly in your callback functions. Hence the neccessity of callbacks.