1

I understand the concept behind callback functions as the following: A function that is passed to another function as a parameter. The idea behind it is that function "A" can use function "B" when event "A" happens, but until then the code can still run normally, instead of waiting for event "A". What I don't understand is some of the syntax and HOW the code actually gets this to work.

So with code like this:

var http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(8080);

console.log('Server running on port 8080.');

I understand that the function (req, res) part is the "anonymous function" which is what is executing the callback function. But I don't know HOW and WHY. Why is the keyword to do this "function", and where are those parameters from? I still haven't come across a good explanation for how this works. I get how it works at a high abstract metaphorical level but I don't understand what the code means.

user35698
  • 87
  • 1
  • 2
  • 5

3 Answers3

0

First, lets take out the anonymous function and make it a regular function:

var http = require('http');

function serverCallback(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}

http.createServer(serverCallback).listen(8080);

Here you can see that the code is calling http.createServer() and passing one argument which is a function reference.

Per the documentation for http.createServer(), when it calls that callback, it will pass two arguments to that function. The first argument is the request object, the second argument is the response object. So, when we declare our callback function, we declare and name those two arguments to our callback so that we can properly use the arguments that are sent to it.

A common point of confusion with Javascript callbacks is that it is the http.createServer() function that decides what arguments to pass this callback and we MUST declare our callback arguments to match what the host function will pass to it. Those arguments names are just convenient programming names that we can use to refer to the two arguments - what we call them does not influence what is actually passed there. So, we must pick names that match what the http.createServer() function is actually passing to the callback.


So, now that you see how the basic callback works, the named function serverCallback can be put inline like this:

var http = require('http');

http.createServer(function serverCallback(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(8080);

I've literally just take the top definition of serverCallback and placed it inline. Note, it even still has the function name. This will work just fine too. But, we aren't really using the serverCallback name so it is isn't needed. That can be removed to end up with this:

var http = require('http');

http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(8080);

This is now an inline anonymous function declaration. It's the same as a regular function declaration, it's just declared inline and the function name is optional.


Now, for your specific questions:

I understand that the function (req, res) part is the "anonymous function" which is what is executing the callback function. But I don't know HOW and WHY. Why is the keyword to do this "function", and where are those parameters from?

function is how you declare a function. This is declaring a function inline so the function keyword is still needed. The parameters are described in my explanation above. They are declared only as convenient monikers for us to use in our code to match what http.createServer() will pass the callback when it calls it. req is a request object and res is a response object and both have their own pages in the node.js documentation as they have both properties and methods.

I still haven't come across a good explanation for how this works. I get how it works at a high abstract metaphorical level but I don't understand what the code means.

Let me know if my above explanation leaves you with any further questions on how the code works.

What I don't understand is some of the syntax and HOW the code actually gets this to work.

If, what you're asking here is how does Javascript call this callback each time some server event happens, then that's a bit of a longer conversation. Behind http.createServer() is some native code that sets up a server. At the TCP level, it defines an incoming port that is listening for new connections on. When one of those new connections occurs (someone from the outside establishes a connection to your server), then the OS services will notify the native code behind this http server that a new incoming connection has been established (e.g. there's a new TCP socket now connected and an HTTP request has been sent over that socket). The native code behind this will then add an event and some data to the Javascript event queue.

If the current JS interpreter is not currently running, then adding something to the event queue will trigger a callback for that event and it will create the appropriate arguments and call the associated callback, executing whatever code you registered as the callback. If the JS engine was currently running some other JS code, then the event will sit in the JS event queue until the current JS thread finishes executing. At that point the JS engine will pull the next event out of the event queue and execute it (calling its callback).

For more about the general concept of the event queue, you can read these other posts:

How does JavaScript handle AJAX responses in the background?

Where is the node.js event queue?

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
0

The req and res parameters (Request and Response) are defined in the node HTTP Server module.

Let's simplify this and make our own callback functions: (not anonymous functions so it should be easier to follow)

function first(callback) {
    console.log("First!");
    callback();
}
function second() {
    console.log("Second!");
}
first(second);

Should output to console: First! Second!

jaggedsoft
  • 3,858
  • 2
  • 33
  • 41
0

A function in JavaScript is an Object. Therefore, you can treat as such and pass it around. It doesn't execute immediately unless you tell it to. It doesn't execute at all unless you tell it to.

So, if I have a function that fetches a dog toy:

function fetchDogToy() {
   trackToy();
   runTowardsToy();
   pickUpToy();
}

and I have a function that brings me back the toy:

function returnToyToOwner() {
  runTowardsOwner();
  dropToy();
}

I probably don't want returnToyToOwner to execute until pickUpToy is finished. So, I will pass returnToyToOwner to fetchDogToy so I can specify when I want it to be executed. Before I can do this though, I need to specify that fetchDogToy is able to take in a parameter, but we know this will be a function object, so just name it accordingly:

function fetchDogToy(callbackFunction) {
   trackToy();
   runTowardsToy();
   pickUpToy();
}

Now, we can pass in a parameter. We know this is going to be a function object, so, let's tell it when we want it to execute:

function fetchDogToy(callbackFunction) {
   trackToy();
   runTowardsToy();
   pickUpToy();
   callbackFunction(); // executed the function object after toy is picked up!
}

So, this is the setup for fetchDogToy. We can call it two different ways. One way is to pass in returnToyToOwner directly when you call fetchDogToy:

fetchDogToy(returnToyToOwner);

However, you might want different behaviour for fetchDogToy after the toy is picked up during different circumstances throughout your application, so we normally just write the raw function object as-is and pass it in that way:

fetchDogToy(function returnToyToOwner() {
      runTowardsOwner();
      dropToy();
  });

It can be named function, or anonymous:

fetchDogToy(function() {
      runTowardsOwner();
      dropToy();
  });

Hope this helps!

On a side note: Consider for a moment fetchDogToy was a third party library function that you read about in some API docs online. You could see now that if they want you to pass a function object as a callback, you really have no idea when they will call the code you are passing unless you read the source code yourself.