How are callback functions' parameters dependent on ordering? - javascript

Let's say I have an event I'm waiting for in Javascript, where we'll have something like this:
fileSystem.readFile('done', function(err, data){
//do something with the file
});
In this situation, the parameters err and data are arbitrarily named and ordered, so they could have been in another order
function(data, err) {/*do something*/ }
How does data get passed the data and err get passed any errors?
I notice this pattern to be pervasive everywhere through out javascript callbacks, and it is largely a mystery to me how function parameters are passed in a predictable order without following some convention I must not be aware of.
How does this work?

What happens is that when you call
fileSystem.readFile('done', function(err, data){
//do something with the file
});
the function you pass as the second parameter will be saved for later use (when the file is finished reading). It will then call the function you passed for you, with a possible error value as the first parameter, and the file contents as the second parameter.
readFile = function(event, callback) {
var error = null;
var fileContents = /* read file implementation. Maybe error will be assigned some value here */;
callback(error, fileContents);
}
Of course, this is just some pseudo-code to illustrate my point.
Now, you can name your parameters in your function function(err, data) whatever you want, because they are in the scope you defined. Even if you swap their names function(data, err), they will still contain the error and file content values respectively.

Related

Wanted help in understanding how parameters are defined in Callback functions, using this example (if possible)

I am trying to understand more on how callback functions work in JavaScript.
I found this simple example code online and after adding the imports & additional txt file it's working. However I am not sure why it is working as the parameters of the functions aren't being defined anywhere?
fs.readFile(`${__dirname}/temp.txt`, (err, data) => {
console.log(`Breed: ${data}`);
superagent.get(`https://dog.ceo/api/breed/${data}/images/random`).end((err,res) =>{
console.log(res.body)
}); })
Upon running this I get the expected result on the console with the line displaying Breed: ${data} and the next line showing the body of the res.
However, the err, data parameters have never been provided to the arrow function within readFile. Neither has the err,res parameters ever been called/provided a value for it to display a message to the console, then how are these lines displaying a message to the console?
I could have understood this working if .readFile returned a err and data value & .end returned a err and res value, however upon reading the documentation both these functions return void, thus i'm confused and help would be appreciated.
You pass a function in to readFile, and later on readFile will call your function. When it calls your function, readFile will pass in the relevant values.
You can do something similar yourself, like this:
function myReadFile(filename, callback) {
setTimeout(() => {
if (Math.random() > 0.5) {
callback(null, 'fake file');
} else {
callback('an error', null);
}
}, 1000);
}
myReadFile(`${__dirname}/temp.txt`, (err, data) => {
console.log(`Breed: ${data}`);
})
(though of course the real readFile function isn't doing timeouts or random numbers, but rather reading from a file and listening for that operation to succeed or error out)

callback function - express MDN tutorial

As I'm currently learning coding with express/Node in order to evolved (used to C and PHP/MySQL...) I have completed the MDN tutorial on express which very well done and every thing is pretty much straight forward; my personnel project is going almost done, thanks to the Mozilla teaching team.
However, here is a point I still can't figure out as I'm still not confortable with the use of Callbacks function.
The point of dealing with asynchronous timing of execution I get, but using MongoDB and mongoose in this tutorial, I've got that queries can be executed either in two steps or in one go by using directly a callback function like creating an instance of a Schema:
// Create an instance of model SomeModel
var awesome_instance = new SomeModel({ name: 'awesome' });
// Save the new model instance, passing a callback
awesome_instance.save(function (err) {
if (err) return handleError(err);
// saved!
});
or
SomeModel.create({ name: 'also_awesome' }, function (err, awesome_instance) {
if (err) return handleError(err);
// saved!
});
However, in the JS script given to fill the DB with data, it seems that both syntaxes are used, here is an example:
function authorCreate(first_name, family_name, d_birth, d_death, cb) {
authordetail = {first_name:first_name , family_name: family_name }
if (d_birth != false) authordetail.date_of_birth = d_birth
if (d_death != false) authordetail.date_of_death = d_death
var author = new Author(authordetail);
author.save(function (err) {
if (err) {
cb(err, null)
return
}
console.log('New Author: ' + author);
authors.push(author)
cb(null, author)
} );
}
What troubles me is that
"cb" is never defined,
the script works the same if I delete all callbacks from arguments "defined" in functions and their respective calling
What is the point of cb(null, author): no data need to be return, they are push to the declared array and saved to the DB at the same time.
The full script can be found here: https://raw.githubusercontent.com/hamishwillee/express-locallibrary-tutorial/master/populatedb.js
Thanks anyone who takes time to read and answer me,
Tiago
Welcome to stack
in Js world , the function can be treated as a variable , the function is a type of variable like String , Number …etc , which may confuse you .. the callback is just a function that has a variable ( argument ) name … now swift language has implemented that pattern as well .
The functions in JS could be sent to other functions as an argument and could be returned as well.
The callback argument sent by the function caller like any other variable sent to the called function but sent as a function …
You could name it anything like cb or whatever … The cb argument is a function which means when you try to use it inside the called function it may need arguments as well like cb(null, author) .. Now imagine the cb is just a variable of type function sent from the caller to the called function.. and the cb itself as a function has to receive arguments .
I know it’s confusing especially if cb is returned as well .
Sometimes callbacks don’t receive any arguments , Again you may call them anything like callback or cb as a convention .. Usually they are the last parameter, as a convention … It just needs some imagination to understand how this confusing parameter goes in and out …
Inside your called function if you didn’t use your callback so removing it will not affect the code as you will not examine errors . BUT in node world callbacks are usually important because it does something after you finish what you’re doing .
i hope that simple illustration clarifies what you’re asked because it has confued me a lot like you .

javascript callbacks - handle reading a file with fs.readFile

For a project, I am using the net module to create a 'mini web framework'
I'm having a lot of trouble dealing with this one callback
var sendFile(path) {
fs.readFile(path, config, this.handleRead.bind(this));
}
where readFile is defined as:
var handleRead = function(contentType, data, err) {
if (err) {
console.log(err); //returns properly
} else {
console.log(data); //returns properly
console.log(contentType) //returning undefined
}
So far this code works in the sense that I can catch errors and also write the data properly.
My question is : how do I send the contentType through the call back?
I've tried -
var sendFile(path) {
var contentType = ContentType['the path type']
fs.readFile(path, config, this.handleRead(contentType).bind(this));
}
But then this causes data and err to be undefined.
I'm quite new to js and am still confused about how to work with callbacks. Any input is appreciated!
.bind() lets you do more than just set the "context" (this value of a function). You can also "bind" arguments in the function.
Try:
function sendFile(path) {
var contentType = ContentType['the path type']
fs.readFile(path, config, this.handleRead.bind(this, contentType));
}
This will pass a callback with its context set to whatever this is and its 1st parameter set to contentType. As long as this callback is called with data (and possibly err), then everything will work.

Node.js - callback parameters

I'm currently learning about callbacks in Node and JavaScript in general and am getting confused by the following:
var request = require('request');
request('http://www.google.com', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body) // Show the HTML for the Google homepage.
}
})
My question is this: How does the request function know what each parameter/argument is in the callback? Because I could effectively call the function callback with two parameters and skip out the error? How would the function know that the first parameter passed was the response and not the error, for instance?
Does it do checks on the types of each at runtime or? Thanks.
The programmer who wrote the request API decides the order of the parameters sent to the callback. There is no type checking at runtime. If you were to supply only two arguments in your callback signature, e.g.
function(response, body) {}
you would still be receiving the error object and the response -- only in this case, the variable named "response" would hold the error object, and the variable named "body" would hold the response. The input you would be skipping would be the third one you didn't supply, the body. The names of your parameters make no difference, it is their position in the function signature that determines what value they will be assigned.
By convention, in javascript callbacks, the first parameter of the callback normally represents the error object. It is not always written this way, but it is a best practice.
Possibile implementation of this function could be:
function request (url, callback) {
// doing something
callback (error, response, body);
}
The function will set the value of error, response and body parameters. I don't know how it's is implemented, but suppose that you pass a number for the url. A possibile implementation could be:
function request (url, callback) {
if (typeof url === 'number') {
// Suppose that error is an instance of Error
callback (new Error ('Url must be a string'), null, null);
}
// other stuff...
}
When you call the request() function, and provide a function as an argument, it calls that function internally (inside the module) and passes in it's own data as the arguments (which you then use to do various things). The data it passes in is data it generated itself, it's not data you're creating.
That is a callback.
There is a quetsion I answered a little while ago that goes more indepth as to why these modules are structured this way, see: npm's guidelines on callback errors
You are using a specific module my friend and callbacks are controlled in this case directly from it.
How the callback works for 'Request' module is matter of research. Check their documentation.

NodeJS Callback Scoping Issue

I am quite new (just started this week) to Node.js and there is a fundamental piece that I am having trouble understanding. I have a helper function which makes a MySQL database call to get a bit of information. I then use a callback function to get that data back to the caller which works fine but when I want to use that data outside of that callback I run into trouble. Here is the code:
/** Helper Function **/
function getCompanyId(token, callback) {
var query = db.query('SELECT * FROM companies WHERE token = ?', token, function(err, result) {
var count = Object.keys(result).length;
if(count == 0) {
return;
} else {
callback(null, result[0].api_id);
}
});
}
/*** Function which uses the data from the helper function ***/
api.post('/alert', function(request, response) {
var data = JSON.parse(request.body.data);
var token = data.token;
getCompanyId(token, function(err, result) {
// this works
console.log(result);
});
// the problem is that I need result here so that I can use it else where in this function.
});
As you can see I have access to the return value from getCompanyId() so long as I stay within the scope of the callback but I need to use that value outside of the callback. I was able to get around this in another function by just sticking all the logic inside of that callback but that will not work in this case. Any insight on how to better structure this would be most appreciated. I am really enjoying Node.js thus far but obviously I have a lot of learning to do.
Short answer - you can't do that without violating the asynchronous nature of Node.js.
Think about the consequences of trying to access result outside of your callback - if you need to use that value, and the callback hasn't run yet, what will you do? You can't sleep and wait for the value to be set - that is incompatible with Node's single threaded, event-driven design. Your entire program would have to stop executing whilst waiting for the callback to run.
Any code that depends on result should be inside the getCompanyId callback:
api.post('/alert', function(request, response) {
var data = JSON.parse(request.body.data);
var token = data.token;
getCompanyId(token, function(err, result) {
//Any logic that depends on result has to be nested in here
});
});
One of the hardest parts about learning Node.js (and async programming is general) is learning to think asynchronously. It can be difficult at first but it is worth persisting. You can try to fight and code procedurally, but it will inevitably result in unmaintainable, convoluted code.
If you don't like the idea of multiple nested callbacks, you can look into promises, which let you chain methods together instead of nesting them. This article is a good introduction to Q, one implementation of promises.
If you are concerned about having everything crammed inside the callback function, you can always name the function, move it out, and then pass the function as the callback:
getCompanyId(token, doSomethingAfter); // Pass the function in
function doSomethingAfter(err, result) {
// Code here
}
My "aha" moment came when I began thinking of these as "fire and forget" methods. Don't look for return values coming back from the methods, because they don't come back. The calling code should move on, or just end. Yes, it feels weird.
As #joews says, you have to put everything depending on that value inside the callback(s).
This often requires you passing down an extra parameter(s). For example, if you are doing a typical HTTP request/response, plan on sending the response down every step along the callback chain. The final callback will (hopefully) set data in the response, or set an error code, and then send it back to the user.
If you want to avoid callback smells you need to use Node's Event Emitter Class like so:
at top of file require event module -
var emitter = require('events').EventEmitter();
then in your callback:
api.post('/alert', function(request, response) {
var data = JSON.parse(request.body.data);
var token = data.token;
getCompanyId(token, function(err, result) {
// this works
console.log(result);
emitter.emit('company:id:returned', result);
});
// the problem is that I need result here so that I can use it else where in this function.
});
then after your function you can use the on method anywhere like so:
getCompanyId(token, function(err, result) {
// this works
console.log(result);
emitter.emit('company:id:returned', result);
});
// the problem is that I need result here so that I can use it else where in this function.
emitter.on('company:id:returned', function(results) {
// do what you need with results
});
just be careful to set up good namespacing conventions for your events so you don't get a mess of on events and also you should watch the number of listeners you attach, here is a good link for reference:
http://www.sitepoint.com/nodejs-events-and-eventemitter/

Categories

Resources