Harke
Harke

Reputation: 1289

Why use callback in JavaScript, what are its advantages?

Could someone explain, why do we use callback in JavaScript? I found examples, but they could be implemented by using the normal functions. What is the advantage of using it? I got answers to "how" to use it and not "why and when" do we need to use it.

Typically, I found it being used in AJAX. on the httpRequest.onreadystatechange. Is this similar to Java's multi-threading? How and where is the listener for the response? Is asyncronous programming akin to multi-threading?

In the following code, how is the flow of control:

function some_function(arg1, arg2, callback) {
  var my_number = Math.ceil(Math.random() * (arg1 - arg2) + arg2);
  callback(my_number);
  some_different_function_not_callback(arg1);
}
some_function(5, 15, function(num) {
   console.log("callback called! " + num);
});

From the JQuery website:

The special thing about a callback is that functions that appear after the "parent" can execute before the callback executes" (ref: http://docs.jquery.com/Tutorials:How_jQuery_Works)

Could someone explain me this line with an example?

Upvotes: 37

Views: 33150

Answers (10)

Bittu
Bittu

Reputation: 1

There is two important aspect of using callback function in JavaScript.

  1. Asynchronous operation: Any function/task which takes some time to execute, callback ensures that it doesn't block the main thread and it keeps our page responsive and time taking task runs behind the scene. once it completed, we can use the result.

  2. Sequential operation: If we have more than one asynchronous operation and we want to execute it sequentially because result of one task determine other task's result.

This is what I understand about the callback. By keeping this two statement in mind, It might help to understand.

Upvotes: 0

Trunk
Trunk

Reputation: 756

Why use callback in JavaScript, what are its advantages ?

Callback functions are basically functions (either named or anonymous) that are passed into a call on another function as an argument. The JavaScript syntax allows us to treat functions as objects so we can pass the name of a previously defined function as an argument of another function without any problem. And we can also pass the full code of an anonymous function as an argument too, e.g. in a XMLHttpRequest response handler for a form field validation process:

req.onreadystatechange = () =>
    {
        if (req.readyState === 4 && req.status === 200)
        {
            if (req.responseText !== 'Valid')                                                   // Check for invalid field value ...
            {
                document.getElementById(field + 'err').value = req.responseText;                // ... and output detail to error field
                document.getElementById(field + 'err').style.color = 'red';                     // Field comment in red
                document.getElementById(field + '-errchk').innerHTML = '\u00D7';                // Cross mark opposite data field
                document.getElementById(field + '-errchk').style.color = 'red';                 // ... in red
                document.getElementById(field + '-errchk').style.fontWeight = 800;              // ... and bold
            }
            else
            {
                document.getElementById(field + 'err').value = '';                              // Clear error info field
                document.getElementById(field + 'err').style.color = 'green';                   // Field comment in green
                document.getElementById(field + '-errchk').innerHTML = '\u2713';                // Check mark opposite data field
                document.getElementById(field + '-errchk').style.color = 'green';               // ... in green
                document.getElementById(field + '-errchk').style.fontWeight = 800;              // ... and bold
            }
        }
    }

In JavaScript the advantage of using a callback function over using a non-callback function (i.e. a function called within another function which does not include it as a parameter) lies in the callback function's range of accessible data, its scope.

Non-callback functions have access to just their own internal scope plus their own global scope plus the parameter values input to them. Beyond this they do not have any unilateral access to the scope of any code block using them. So if we want to have a non-callback function "see" certain vars/lets/consts/functions/collections within the code that calls it, we need to move the declarations of these within the scope of the non-callback function. This is always a bit cumbersome and sometimes very tricky. Otherwise we may need to redefine the non-callback function so that otherwise non-shareable data from the calling code's scope is passed as parameters to it. This of course changes the declaration of the non-callback function and impacts any other code block that uses it.

Callback functions have access to both their own scope plus the scope of the code that calls them plus the global scope of the calling code. So callback functions are handier to manage codewise, especially in larger JS applications where data is passed across several module boundaries during execution of a task.

Callback functions existed in JavaScript from almost the beginning. At the front end of web applications, you'll see them used in things like event listeners, for example if we want to remove the mouse's on-hover text displayed when we hover away from the "Next Month" arrow of a web page calendar :

const mouseOut = (e) =>
{
    if (document.getElementById("right-info").innerHTML != "")
    {
        document.getElementById("right-info").innerHTML = "";   
    }
};

nextM.addEventListener('mouseout', mouseOut, false);

At the back end of web applications, callbacks were used for asynchronous data transfer between browser and server. They were also used as the original means of ensuring back-end JS scripts that included asynchronous functions (e.g. database queries, network calls, file-system actions, data loading from external devices, etc) would execute in the desired order. Basically, by including a callback function that does the operation needing to be done immediately after the async process as a parameter to the async function, we could ensure its correct sequencing w.r.t. the async process. The scoping privileges of a callback function helped the coding a bit too.

Nowadays use of callback functions at the back end is seldom used as modern JavaScript includes a Promise object that encapsulates handling of asynchronous processes in a clearer and more scalable way.

But it's still good to know what they are and where they can be effectively used.

Upvotes: 2

David Ly
David Ly

Reputation: 31586

Callbacks allow single-threaded operations (Javascript is single-threaded) to execute asynchronously.

The most obvious example is AJAX calls, where you have a callback that executes after the AJAX request is done. The AJAX request can take a while, and if it were a normal function call, the whole page would be frozen (can't scroll, can't select text, etc) while the request loads.

This is achieved through either setTimeout or setInterval which enqueues a function to be called at a later time while allowing other code to execute in between. So when you're waiting for that AJAX call to finish, other code (which includes the browser updating) is allowed to execute.

Since you want examples other than AJAX, the other common usage for the async nature is for animations. Callbacks are required for animations because it needs to allow the UI to draw.

Say you wanted to animate a div 100px to the right over 5 seconds. Your first instinct might say create a loop and sleep in between. But there is no sleep in Javascript, and even if there was, it would just freeze the UI because nothing can happen while it's sleeping.

Instead, you need to do something along the lines of increment the position by 10, then call setTimeout for 500 ms with a callback to execute the next frame. This would probably be done recursively.

An additional use would be just simply to pass functions as parameters, although I'm not sure if the term "callback" is appropriate for that use case. That is the way you've used it in your example, some_function can be reused with a variety of functions used as callbacks, so sort of injecting code.

Upvotes: 2

Cheeso
Cheeso

Reputation: 192457

Is asynchronous programming akin to multi-threading?

Yes.

Javascript's asynch model provides a way to do work "in the background".

Suppose you have a long-running calculation. This might be hard to imagine for a simple js demo, but imagine a long decompression routine, or a long-running pathfinding algorithm in a game, etc. some numerical calculation that takes more than a second.

Doing the calculation by directly invoking the function will work, but it will suspend the browser UI for the duration of the calculation. Running things asynchronously means the browser UI remains responsive, as the calculation continues running on a background thread. When the calc completes, according to the asynch programming pattern, the function invokes the callback function, notifying the application layer that the calculation is complete.

Upvotes: -7

mohit dutt
mohit dutt

Reputation: 43

Callback itself, as the name suggests (call - back) whenever you need to call a function after the execution of the first one, in those scenarios we need to use a callback. Basically the use of callback helps us when we need to use the result of the first function into another function and of course we can use it in a direct way but what if the first function did not respond to any result and we have already passed it to lower function then it will results to undefined and later if one had tried to mutate that passed value it will result in error like can not mutate or assign value of undefined .

function first(){
  //any call or request that is giving us result say abc
  function second(abc){
    //mutate abc, it can be either array, object or can be another function
  }
}

In these scenarios, we need to use call back as the first function will result in abc but no one knows how much time it will really take.

and apart from callback one should use promises or async/ await to make your code more modular and looks more like in synchronous ways

Upvotes: 1

Vizz85
Vizz85

Reputation: 197

Another point is code testability.

Let's say you have this scenario:

function pushToDatabase(data) {
  // do some API call...
}

function handleData(someData) {
  // some logic

  pushToDatabase(someData);
}

When unit testing maybe you don't want to look into the database if new data is available but just check if pushToDatabase function was called with the right data, so it can be refactored to:

function pushToDatabase(data) {
  // do some API call...
}

function handleData(someData, callback) {
  // some logic

  callback(someData);
}

and called with handleData(someData, pushToDatabase).

This could be a test with Jest:

const aModule = require('./aModule');

describe('aModule', () => {
  it('should push data to a database', () => {
    const mockFn = jest.fn();
    const myData = 'Hello!';

    aModule.handleData(myData, mockFn)

    expect(mockFn).toHaveBeenCalledWith(myData);
  });
});

Link to working Repl.

Upvotes: 0

EMI
EMI

Reputation: 826

The main browser process is a single threaded event loop. If you execute a long-running operation within a single-threaded event loop, the process "blocks". This is bad because the process stops processing other events while waiting for your operation to complete. 'alert' is one of the few blocking browser methods: if you call alert('test'), you can no longer click links, perform ajax queries, or interact with the browser UI.

In order to prevent blocking on long-running operations, the XMLHttpRequest provides an asynchronous interface. You pass it a callback to run after the operation is complete, and while it is processing it cedes control back to the main event loop instead of blocking.

There's no reason to use a callback unless you want to bind something to an event handler, or your operation is potentially blocking and therefore requires an asynchronous programming interface.

This is an excellent video discussing more about the event loop used in the browser, as well as on the server side in node.js.

EDIT: that convoluted line from the jQuery documentation just means that the callback executes asynchronously as control is ceded back to the main event loop.

parent_function(function () { console.log('Callback'); });
parent_doesnt_block(); // <-- function appears after "parent"
therefore_execution_continues();
// Maybe we get 'Callback' in the console here? or maybe later...
execution_still_continues();

Upvotes: 26

outis
outis

Reputation: 77400

Callbacks are used all over the place in JS, particularly in jQuery.

One place you need to use a callback is anywhere the language doesn't give you coordination. Coordination means code doesn't execute until the data it needs is ready. Synchronous calls are coordinated; the call doesn't return until the sub-computation is finished. Asynchronous calls don't give you coordination, so you need a callback.

  • Event-driven computation can be uncoordinated, so event handlers are callbacks:

    <input id="a" /> <button id='add'>+</button> <input id="b" /> = <span id="c"></span>
    <script type="text/javascript">
      $('#add').click(
          function() {
              $('#c').text(parseInt($('#a').val()) + parseInt($('#b').val()));
          }
      );
    </script>
    

    In JS specifically, coordination for event dispatch (e.g. the DOM dispatchEvent, jQuery trigger and IE fireEvent methods) is left unspecified (except for nested DOM events, which are synchronous). If you trigger an event, the handlers may be deferred and execution immediately return to whatever is after the trigger. Event handlers are usually called synchronously, but they don't need to be.

  • JQuery effects usually take a callback to execute once they finish. Animation functions are asynchronous so that they don't block the rest of the script.

Callbacks are also useful for functions that define the outer part of some computation, but leave an inner part undefined. Here are some examples:

  • You can use callbacks to filter collections:

    // get odd items
    $([0,1,2,3,4,5,6,7,8,9]).filter(function (i) {return this % 2;})
    // or:
    $.grep([0,1,2,3,4,5,6,7,8,9], function (x, i) {return x % 2;});
    
  • Array.sort takes a callback so you can define how elements are compared.

    [{name: 'foo', id: 1}, {name: 'bar', id: 5}, {name: 'baz', id: 3}]
        .sort(function (a,b) { 
            return a.name.localeCompare(b.name); 
        })
    

Some of jQuery's DOM manipulation methods (such as append, prepend and wrap take a callback to construct an element based on context provided by the jQuery method. You could view the callback as providing an inner portion of a computation or as a matter of coordination: when the outer computation is started, the needed data to build the new DOM elements isn't available; the callback finishes the sub-computations to create the elements when the data becomes available.

setTimeout and setInterval both take callbacks to execute after a delay.

Starting with version 1.5, jQuery offers deferred objects as a way of managing multiple callbacks, with various execution dependencies between the callbacks.

A callback is very similar to a "continuation", which basically means "the rest of the computation". The difference is that a continuation represents the entirety of the rest of the computation while a callback represents the rest of a sub-computation. Continuations are part of a whole style of programming known as "continuation passing style" (CPS). With continuations, you can create all sorts of interesting control structures. For example, continuations can be used to implement exceptions and coroutines.

Depending on the features of the language engine (in particular, you need tail call optimization), CPS can offer a more efficient approach. Some control structures (such as coroutines) require tail calls, otherwise you'll get a stack overflow*.

Upvotes: 3

Alex Wayne
Alex Wayne

Reputation: 187004

Not quite like multithreading...

You use a callback anytime you need to wait on something external to your primary JS code. In a browser this is used a ton for AJAX, and in node.js it's used for every single thing that calls out to the system (file access, networks access, database requests, etc).

Let's say you want to fire an ajax request everytime a user clicks a button. Now lets say that ajax request takes 10 seconds to complete. The user then clicks 10 of these buttons before those 10 seconds are up. This would repeatedly call a function like this:

var clicked = function() {
  doAjax('/some/path.json', function(result) {
    updatePageWith(result.widgets);
  });
};

This runs code in the JS engine for only long enough to make the request. Then it idles while it waits. Other JS can run at this point, the UI is totally fluid and interactive, everything is awesome. Then suddenly, all 10 of those requests resolve at once. And then our callback is invoked 10 times like magic.

This works because every time we call clicked() we are creating a new function object, and passing it to the doAjax() function. So there are 10 unique callback function objects hanging out in memory, each one bound to a specific request by the doAjax() function. When a request returns, it finds the associated callback object and calls it.

The huge advantage here is that, although javascript is single threaded, you never tie up that thread with waiting. If you JS thread is busy, it should only ever be because it's actively running code. So even though JS is single threaded, it's trivial for your code to implicitly hold the state of any number of any kind of asynchronous tasks.

The synchronous method of callbacks are used for a different purpose usually. Like listeners or delegates. Like telling object A to callback when it's data changes. While not strictly asynchronous, you usually aren't calling that callback immediately. Instead it will be called later in response to some sort of user action of event.

Upvotes: 10

GBa
GBa

Reputation: 18387

Because the javascript being executed is Asynchronous, therefore if you just put any old function after making the asynchronous request, it will likely be called before the original request completes. The original request will return as soon as it BEGINS (is sent out), not completes.

If you need to do something with the result of the asynchronous request, or chain together requests, etc you will need a callback to ensure the next step doesn't begin before the previous step is finished.

Upvotes: 6

Related Questions