简体   繁体   中英

JavaScript: How does a callback function work?

I'm really new to JS and I'm having a lot of trouble writing/understanding callback functions Let's say for example, I have the following code, but i dont want

takeNumbersGreaterThan(5);

to be executed until

 insertNumbers();

is finished

numbers = [];   
 greaterThan = []; 
insertNumbers();
takeNumbersGreaterThan(5);

insertNumbers(){
  for (var i = 0; i<11; i++)
  {
    numbers.push(i)
  }
}

takeNumbersGreaterThan(number){

 for (var m = 0; m<numbers.length; m++)
  {
    if (numbers[m] > number)
      {
         greaterThan.push(numbers[m])
      }
  }
 }

How do I go about doing that?

The basics (not about callbacks, but about programming languages)

To understand callbacks first you have to understand functions. And to understand functions in javascript you first have to understand variables, values and functions.

Almost all programming language can deal with values. So if you've done any programming you'd have a basic idea of what values are (I'm going to greatly simplify types of values here and refer to both values and references/pointers as "values").

A value is a thing. For example a number or a string. So 22.31 is a value and "Hello Dave" is a value.

Most languages also have the concept of variables (not all do though). A variable is a "name" we give to values to make it easier to process values. For example, in the following, x is a variable:

var x = 12;

.. and it's value is 12.

What do variables allow us to do? It allows us to substitute a value for a name in our calculations. Just like math. For example, if x is 12 and we know we can add 1 to 12 we can also do:

x + 1

Functions are values

In javascript functions are values. For example, in the following we assign a function to a variable:

function a () {return "Hello"}
var y = a;

Since what variables do is to allow you to substitute a name for a value then if you can call the function a using the syntax a() it means you can also do this with the variable y :

y(); // returns "Hello"

Callbacks

If functions are values it also means that you can pass functions as arguments to other functions. For example, the following is how you'd normally call a function in another function:

function a () {return "Hello"}

function b () {return a() + " World"}

b(); // returns "Hello World"

If you can pass functions as a variable, it means you can do something like this:

function a () {return "Hello"}
function b () {return "Bye"}

function c (functionVariable) {return functionVariable() + " World"}

c(a); // returns "Hello World"
c(b); // returns "Bye World"

As you can see. Callbacks are not special at all. They're just the result of the fact that in javascript functions obey the same rules as other values like numbers, arrays and strings.

Callbacks does not mean asynchronous

As you can see from the example above, both calls to the function c return a value. Thus the function c is not asynchronous even though it accepts a callback. So callbacks can be used for both synchronous and asynchronous code.

A good example of a synchronous callback is the Array.sort() method. It sorts an array but accepts an optional callback for you to define how to sort (alphabetically, numerically, by last name etc.).

Why asynchronous code need callbacks

For now forget about ajax or networking code. Let's instead look at a scenario that makes it even more obvious why callbacks are used by asynchronous code.

Say for example you have a button. Now, when the user click this button you want something to happen. How do you do that?

The first thing most people do is probably something like this:

while (1) {
    if (button.isClicked) {
        doSomething();
    }
}

OK. So that's an infinite loop that only checks if the button is clicked and nothing else. Then how do you expect the browser to update the UI and track the mouse? This leads us to the next thing people try to do:

while (1) {
    if (button.isClicked) {
        doSomething();
    }
    else {
        updateUI();
    }
}

OK. Great. But two problems. First, if someone were to write a library like Google Charts or jQuery or anything to do with the UI they either will write their own while(1)... loop or you must manually copy/paste their function into your while loop. This does not scale. Second and more importantly, this is inefficient. That while loop will use 100% CPU time checking a button. Wouldn't it be nicer if the browser can tell us when the button is clicked.

Fortunately in javascript functions are just values. You can pass a function to the browser and tell it to execute your function when someone clicks a button:

button.addEventListener('click', doSomething);

Note: Notice the difference between treating a function as a variable and calling a function. If you want to treat a function as a variable just use the name. If you want to call a function use braces like doSomething() .

Why everyone insist on writing asynchronous functions

There are two reasons why everyone seem to insist on making asynchronous APIs, especially in languages like javascript.

First, the low-level file and network I/O are async. This means that if you want to talk to a database or a server or read a file you need to implement it as asynchronous. Also, javascript is single threaded. So if you use the synchronous versions of I/O functions you will freeze everything else.

Second, it turns out that in a lot of cases (but certainly not in all) asynchronous, single-threaded programming is as fast as or sometimes even faster than synchronous multi-threaded programming.

Combined, the two reasons above creates social pressure in the js community to ensure that all I/O code are asynchronous in order to maintain the speed advantage and not block other people's code.

If I understand correctly, you want to know more about callbacks and you want to use them. Let me try too help you using your code.

If you want to execute takeNumbersGreaterThan(5); right after insertNumbers(); using callback function, you could do something like this:

numbers = [];   
greaterThan = []; 
insertNumbers(takeNumbersGreaterThan, 5);

function insertNumbers(callbackFunction, callbackFunctionParam){
  for (var i = 0; i<11; i++)
  {
    numbers.push(i)
  }
  callbackFunction(callbackFunctionParam);
}

function takeNumbersGreaterThan(number){

 for (var m = 0; m<numbers.length; m++)
  {
    if (numbers[m] > number)
      {
         greaterThan.push(numbers[m])
      }
  }
 }

but this is just a simple example of how you can call a callback function after some computation. This code could be improved. The point is, you can pass your callback function as parameter on your function and then later execute this callback function.

You are already there. Your code is almost completely correct. You was just missing function keywork declaration.

The script below, shows you how to run takeNumbersGreaterThan after insertNumbers. In my sample I also changed the function sign in order to pass array as parameters and avoid some of one common "mistakes" known as closures.

 var numbers = []; var greaterThan = []; var insertNumbers = function(numbers) { for (var i = 0; i<11; i++) numbers.push(i) } var takeNumbersGreaterThan = function(number, numbers, greaterThan){ for (var m = 0; m<numbers.length; m++) { if (numbers[m] > number) greaterThan.push(numbers[m]); } } // run insert numbers insertNumbers(numbers); // run take numbers greater than takeNumbersGreaterThan(5, numbers, greaterThan); // log document.write(greaterThan);

Your code dosen't use any asyncronous calls so you wouldent need to use any callbacks to handle the execution. But if you would like to know how to do it, this would be the way.

numbers = [];   
greaterThan = []; 

function insertNumbers(callback){
  for (var i = 0; i<11; i++)
  {
    numbers.push(i)
  }

  callback(); // now execute the callback funtion
}

function takeNumbersGreaterThan(number){

 for (var m = 0; m<numbers.length; m++)
  {
    if (numbers[m] > number)
      {
         greaterThan.push(numbers[m]);
      }
  }
  console.log(greaterThan);
 }

 insertNumbers(function() { // here we send a functions as argument to insertNumbers which will execute when callback() is called
  takeNumbersGreaterThan(5);
});

insertNumbers takes a argument called "callback". When insertNumbers is finished we simply run callback() . In the initial call to insertNumber we pass a function as argument which will be executed as soon as insertNumers finished (or callback() is called).

Code (for the most part) is executed sequentially. In the code you've provided, the computer runs though the code in the order you've provided it. First it creates a new array object and sets it to the numbers variable, then it creates a new array object and sets it to the greaterThan variable. Then, it runs the insertNumbers function. Now what the computer does is jump to the code that you've defined in insertNumbers and executes all that code. Then, after it finishes with that, it'll return to executing the initial thread of code it was on which is back at line 4. So now it'll jump to the takeNumbersGreaterThan code. So functionally, you don't need any callbacks since your code doesn't do anything that takes an arbitrary amount of time.

That being explained, you see that takeNumbersGreaterThan doesn't get executed until after insertNumbers is executed.

The only time code isn't executed sequentially is when you start doing multi core/threaded code.

Callbacks are used when something takes an arbitrary amount of time like when you are reading data from a disk or are requesting data from the network.

Callbacks can exist, because functions defined in javascript (and many other langauges) exist as objects in the code. If you don't put the parentheses after a function name, you're actually referencing the function object just like any other variable. So you can pass that function object around in your code and to other bits of code. That is what is happening in this example.

setTimeout(myCallback, 5000)

function myCallback(){
    console.log("5 seconds have passed");
}

So, as you can see, I can take my function myCallback and give it to another function, in this instance setTimeout , to use after the other function has completed a task.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM