Callbacks in javascript and understanding to chain them - javascript

I have a function which i am chaining into my callbacks in my node.js apis. While writing this for first time we had no scenario that it would be called individually too or as first method of chaining callbacks. So we wrote it like below
processResults: (data, res, cb) => (error, response, body) => { // dostuff
// with response and process it
}
Now when the scenario to call processResults as the first method of callback chain arrived we did it like below
app.post('/results, (req,res)=> {
// call process results
ctl.processResults(req.body,res,terminalCb)***()*** ;
// () these seem to be wierd
})
And to my surprise it works and now i am really confused how does this magic happens and somewhere i feel it is not the correct program writing technique. I really need help on this. Please if someone could explain or suggest a right way to code this or if this is the right way then why?
Please help.

You're just calling the inner function with no arguments: (error, response, body) will all be undefined, but it's permissible to do that just as it's permissible to call arr.forEach(elm => with only one argument instead of three.
But if, as you said, the inner function does stuff with response, it won't work the way you typed your code because response will be undefined. res will be defined though, since you did pass that as a parameter to the outer function.
It would be nice if you could provide access to the default function directly, rather than only having a function that returns a function, like this:
const ctl = {
processResults: (data, res, cb) => (error, response, body) => { // dostuff
// with response and process it
}
}
ctl.processOne = function(data, res, cb) {
return ctl1.processResults.call(this, ...arguments)();
}
You still have the ugly () but it's abstracted behind the object you're interfacing with.

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)

JavaScript parameter used in arrow function but not declared anywhere in local scope - Twilio function

I'm working on a pre-existing Twilio function and am trying to understand how the following code works. I've tested it a few times, and as long as I send the 'from' phone number any message, I get the response described in the 'body'. However, I'm trying to understand how the 'msg' and 'err' parameters can exist, given they aren't declared elsewhere in the function. Can someone help me understand how these parameters are valid? I'm new to JavaScript and only understand the basics of callbacks and promises.
EDIT: I'm starting to understand the idea more. So the 'then' and 'catch' statements are callbacks that occur after the create() function finishes. Thank you for the answers that helped me understand.
exports.handler = function(context, event, callback) {
context.getTwilioClient().messages.create({
to: '+14806484732 ',
from: '+12133151685',
body: 'Remember the special today is Shrimp Scampi'
}).then(msg => {
callback(null, msg.sid);
}).catch(err => callback(err));
}
err => callback(err) - this is a declaration of function. single parameter err, executing function callback
similar situation with
.then(msg => { // `msg` is a single parameter of declared function
callback(null, msg.sid);
}
Example
const callback = argument => console.log(argument)
// this line is similar to
// const callback = (argument) => { console.log(argument) }
// also similar to
// function callback(argument) { console.log(argument) }
const func = err => callback(err)
// this line is similar to
// const func = (err) => { callback(err) }
// also similar to
// function func(err) { callback(err) }
func('test')
The functions given in then and catch are callbacks that both take a single parameter. Each of these parameters have a specific meaning based on the function documentation, which you can look up. You are, however, free to call them whatever you like if you were to write the callbacks yourself. The names are simply chosen to convey the semantics as clearly as possible.
Thus, they are essentially just parameters of an anonymous function.
I strongly recommend looking into callbacks in Javascript, as they are used everywhere.

Using `replace` with Node.js function

I'm trying to write some code which will take a string containing partial URLs like google.com and will transform them into a full URL like https://google.com.
I'm very inexperienced with Node.js and I'm still trying to get my head around the idea of asynchronicity.
I'm attempting to use callbacks instead of stuff like promises and async/await, and in my head the following code should work:
exports.rectifyDocumentURLs = function(baseUrl, document, callback) {
callback(null,
document.replace(url_patterns.any, (match) => {
return exports.fixUrl(match, baseUrl, (err, res) => {
if (err) {
callback(err, null)
}
return res
})
})
)
}
url_patterns.any is some regex code which will match any type of code, and the function exports.fixUrl is a function which will take a partial URL and return it in its full form.
When I run the code like so
exports.rectifyDocumentURLs("https://google.com", "google.com", (rectifyErr, rectifyRes) => {
console.log(rectifyRes)
})
The current code just returns undefined, but the res of the fixUrl function returns the correct result, http://google.com.
I understand that this a lot like many of the questions on here, but after extensive research and many tries and rewrites, I believe this may be the only way to fix the code.
Any help would be greatly appreciated.
You can use the url module who provide utilities for URL resolution and parsing.
const url = require('url');
const myURL = new URL('https://google.com');
console.log(myURL.host); // google.com
Your function rectifyDocumentURLs() is all messed up.
The idea of the callback is that when something will happen asynchronously you don't know when it will be finished. So you pass it a function, when it's done it calls the function you passed it with the result you want. Typically functions that take callbacks never return a meaningful value -- the value is passed to the callback. In rectifyDocumentURLs() you are calling the callback immediately, then calling it again with the callback of another function and also returning a (maybe undefined) value. That's just not going to work.
Here's a more standard approach:
exports.rectifyDocumentURLs = function(baseUrl, document, callback) {
// it looks like document.replace is async and takes a callback
// when it's done it will pass its result to the callback as match
document.replace(url_patterns.any, (match) => {
// you don't need return the result of a function that takes a callback
// it will pass its result to the callback
exports.fixUrl(match, baseUrl, (err, res) => {
if (err) {
callback(err, null)
} else {
// now that you have res and no error, call the callback
// you passed to rectifyDocumentURLs()
// this value will be the rectifyRes in the function you passed
callback(null, res)
}
})
})
}
Of course, as others pointed out, there is already a lot of existing code for handling URLS, but this is a good exercise.

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/

Why cant I pull the callback out of the app.get function in node and express.js?

I'm trying to do this in my server.js file:
// doesn't work... don't know why.
var findBooks = function (err, books) {
if (!err) {
return response.send(books);
} else {
console.log(err);
}
}
// Get all books
app.get('/api/books', function (request, response, err, books) {
return BookModel.find( findBooks(err, books));
});
and it gives me a 404 error when I run a jQuery xhr in the console, and the same error when I request the page in the browser. It works if I put the callback in the usual place, moving the findBooks function into the BookModel.find function where it's called and deleting the last two arguments of app.get. I'm working though Chapter 5 of Addy Osmani's Backbone.js Applications book to give you some context. Why doesn't this work? Is doing this a good thing, or am I trying something that isn't recommended?
You have two problems:
findBooks tries to access response, but response is not in scope at that point.
You're calling findBooks with err and books and passing the return value to BookModel.find, when you probably want to pass the function for find to call.
Let's first solve the second problem. Rather than calling the function, just pass it:
return BookModel.find(findBooks);
That was easy. Now, to solve the first problem, we'll need to add an extra argument to findBooks. I'll assume you put it at the start for reasons I'll detail later. Then we can change the usage to look like this:
return BookModel.find(function (err, books) {
return findBooks(response, err, books);
});
It turns out, though, that there's a more concise way to do that, using bind. bind is best known for its use in binding the value of this, but it can also be used to bind arguments:
return BookModel.find(findBooks.bind(null, response));
All together, we get:
function findBooks (response, err, books) {
if (!err) {
return response.send(books);
} else {
console.log(err);
}
}
// Get all books
app.get('/api/books', function (request, response) {
return BookModel.find( findBooks.bind(null, response) );
});

Categories

Resources