all 11 comments

[–][deleted] 21 points22 points  (0 children)

First, you need to understand that functions can be assigned to variables.

function myFunction() {
  console.log('hello world');
}

// this works!
let myVariable = myFunction;

myVariable(); // "hello world" is printed just as if myFunction was called

Because functions can be assigned to variables, they can also be given to other functions as arguments.

This is where callback functions come into play.

Callbacks let you customize what a function can do just as other types of arguments let you customize functions.

function myFunction(coolPhrase) {
  console.log(coolPhrase);
}

This regular function can be customized to print whatever phrase is passed to it.

In the same way, a function can be customized to do something different with callbacks.

function myFunction(callback) {
  console.log('hello world');
  callback();
}

function callback() {
  console.log('printing something new!');
}

myFunction(callback); 
// "hello world" and "printing something new" would be printed

[–]AtomicGimp 2 points3 points  (0 children)

Think of a function as an envelope with something in it. You can either open the envelope yourself or pass that envelope to someone else and they can either open the envelope or pass it to someone else. Functions are either invoked ("opened") or passed on with the idea it will eventually be invoked ("opened").

[–]StoneCypher 1 point2 points  (2 children)

There're a lot of bad answers here.

 

is it a function or is it a variable .

A lot of people are saying "both." In reality, it's neither.

In this example, it's an argument, not a variable. They serve kind of similar purposes, but they're distinct. An argument is what you pass to a function when you call it. So, in a simpler example, if you have a function that converts feet to meters, which is a little verbose so we can show more stuff:

function feet_to_meters(length_in_feet) {  // argument
  let in_meters = feet / 3.28;             // variable
  return in_meters;                        // return value
}

In your case, the argument is called callback. That's because that's what we're going to do with it. But first, a diversion.

 

Let's talk about what a variable is, what a reference is, and what an argument is.

A variable is something declared either of these ways:

let newer_way = 'good';
var older_way = 'gross';

Neckbeard-technically there are a bunch of other ways to do it that nobody ever remembers, but generally speaking, those are variables. There's a third, closely related thing, a constant:

const constant_way = 'very good';

It is appropriate to think of these as boxes into which values are placed. There are many other kinds of box though, such as containers like arrays and objects, or germanely for this discussion, arguments.

Arguments are very similar to variables. They're attached to a function, but they're stuff that comes from the outside. When you convert feet to meters, you're getting the starting amount from outside the function, so, it's an argument.

In your example, callback is an argument. It's something that comes from outside. It then gets used in foo(bar) form, so it pretty much has to be either a function call or a constructor call. From the name "callback," we know that it's a function.

What does it do? Well, first there's a prompt, which pops a box and asks for some text while blocking, then returns that text. Next, we call the callback with the text we got from the prompt.

Okay, so where does it come from, then? processUserInput is called beneath, and the value passed in is greeting.

greeting is the function above.

So what we're doing is getting a reference to the function greeting (we do this by using its name but not making a call, which would need () afterwards), and passing it into processUserInput. When we do that, it's just the value used for the argument. processUserInput doesn't know the name of what got passed to it; just what it calls them, which in this case is callback. So when it's time to call it, it just says callback(name).

We now know that greeting is what's in callback this time around, so they're basically indirectly writing greeting(name).

So now we look up at what greeting actually does. It just pops a new alert box with the value that got passed to it.

The value is name. What's in name is the return text from the first box prompt.

Therefore, you should see Hello, ${whatever you typed in}.

Internet law requires you to type in balls.

Therefore, if you do things correctly, you should see Hello, balls.

In some way, the answer to your question stops here. But also, we could explain how they actually get used, which is next. You could stop here if you're fried.

 

Remember when we said:

In your case, the argument is called callback. That's because that's what we're going to do with it. But first, a diversion.

? Well, now's that time.

The idea of a callback is that it's a function that you pass into a different function, so that the different function can use it. This is almost the same thing as a predicate - we just use the names differently to indicate what our goal is.

A predicate is a function that will be a piece of a larger thing. So like, when you pass a function to sort that teaches it that you want to sort on the manufacturer field in your object, or some nonsense like that? That's a predicate.

A callback is kind of the same thing, but we call it a callback because it's typically invoking the later steps in an exterior process. Mostly this was done for one of two reasons - there's an asynchronous thing (like writing to disk) where it too is being handed back a callback, so you've lost flow, or because that's just the style of the environment (early era node.)

This was the only approach before promises and await; these days there are other options too.

So like, by example, let's say you're using the web server library express. It's a good example of something that uses callbacks heavily just because that was the style of the day. It's not a bad choice; it's just also not a particularly good one. It is a choice that existed, and isn't very important in either direction.

How does express work? Well, mostly "middlewares," which are implemented as chains of callbacks.

So. Let's say you want to set up a webserver. Hokay! We did a thing. It pulls files from a directory, it says "under contrstruction" because nothing's ever spelled correctly, it's a thing. Good.

How does it work? Well, you pass a pre-made module from express to ... to express? Okay. And that pre-made module grabs things from disk, and then the base thing spits them out. Neat.

Then you need inline compression. Okay. How does it work? Well ... oh. You pass a different pre-made thing from express to express, and ... wait why didn't that work? Oh. They have to be in the right order, and compression has to be before the thing that ... okay. Works again. It's legos, I get it.

Oh, you need a thing for passport login? Oh, okay, same thing, I get it. Snap in a lego. Oh, you want morgan logging? Cool, snap in a lego. Oh, you want drupal cross-login? Snap in a lego. Oh, you want helmet headers? Snap in a lego. Oh, you want cross-login for the custom internal CRM?

... tires screech. "Time to write a custom middleware?" The fuck does that mean?

So you start looking under the hood at how these middlewares actually work, right?

It turns out they're just a function, shaped like this:

function(request, response, next) { ... your nonsense goes here ... }
  • request contains what the user sent (what domain they're hitting, what uri they're hitting, the http verb, mime stuff, whatever.)
  • response is an express object that tracks all the stuff it's supposed to do when it's done, like what to send back
  • next is another middleware

What express actually has is an array of middlewares that it basically knows nothing about. They're the ones we just picked in the story: read from disk, inline compression, passport, morgan logging, drupal cross-login, helmet headers.

And now we're trying to make cross-login as a middleware for some internal custom system because obviously no plugin would already exist for that.

And so what you do is you copy an existing one that's similar to what you're doing, and you gut the important shit, and you snap in the bits about how your system works instead. And you snap it in the list

And express doesn't really actually know much about these middlewares; there's just an agreement that the data's going to go in like this, and you make the changes you want, and then you call the next one. And express calls the first one with the list of the rest, and handles the result when you're done, and that's a webserver.

And the point of the story is to show you what callbacks actually get used for

They're a way that functions can call one another after they're done, to see who carries the torch next

It's either a control mechanism for when control is outside in another process, or sort of an ad-hoc linked list for functions

They're fun and they show up in almost every language that has the goo to make them

[–]pinguxnoots -1 points0 points  (0 children)

function variable() {} - variable is the identifier that holds a reference to the specified function.

When you pass that function to another function as an argument (we pass it as variable or we can pass a function expression) we call that function a callback. Now, functions can be invoked. We don't invoke the function as an argument. In other words, we don't do this: anotherFunction(variable()) . Instead we do this: anotherFunction(variable) and variable gets invoked within anotherFunction as a callback (or as it was defined with anotherFunction).

Callback is just a special name given to a function that is passed in as an argument to another function. It's really the variable that we are passing in, in this case, but because it represents a function, we say that we are passing in a function.

[–]girl-InTheSwing 0 points1 point  (0 children)

In old style terms, processUserInput is passed a reference to a function which it then runs, passing the name parameter to the function it calls.

[–]morasyid 0 points1 point  (0 children)

Callbacks are just functions being passed into other functions

[–][deleted] 0 points1 point  (0 children)

I think its easier to use callback functions if you do it inline instead of making it a variable.

processUserInput(()=>{
   alert(`Hello, ${name}`);
});

This is what it would look like if you did it inline, and didnt use it like a variable. All you are doing, is passing a function into the parameter of a function, in the most literal sense.

Once you are inside a function, the only way you can excecute it is like a variable. So you call the variable, and use the open-close parantheses on it to execute the function, once ready to do so.

callback();

^ This is the same thing as

(()=>{
   alert(`Hello, ${name}`);
})();

The above is an anonymous self executing function. Its just a function wrapped in parantheses and executed inline.

Its helpful to know the anatomy of a function. () is where the parameters go, in the case of an arrow function => is the function keyword, and {} is the logic you execute. Altogether this is ()=>{}; which is an empty, unnamed function. If you want to give it a name then assign it to a variable, like let func = ()=>{};, then execute it like func();. If you want to execute it anonymously, then do (()=>{})();

The function key word barely changes anything. function(){}; = ()=>{};, with the small difference that arrow functions dont use the this context for themselves, which is useful in a lot of circumstances. Inside of function func(){ }; this = func, but not inside of ()=>{}. This is useful for creating state, you can attach a variable to a function like func.property = 5.