Usefulness of callback functions

2020-04-05 17:52发布

问题:

In Javascript there is a possibility to define a function X and pass it as an argument to another function Y.

Such a function X is called a callback function.

Could you explain why is useful to use callback functions in some clear examples (e.g. send some fiddle links with demonstration)? I can see one usefulness, it's code readability, but I'm not sure in this, because code with callbacks looks more complex.

The only usefulness is it's use in browser environment and asynchronous execution in AJAX? What about another Javascript implementations (e.g. Rhino)? Are callbacks useful there as well? Or does their usefulness depend only on the environment where Javascript is executed?

thank you

回答1:

Callbacks as parameters

Callbacks are useful because they allow you to "configure" a function with code.

The archetypal example for this is a sorting function which allows you to specify your own sort criteria. I 'm going to use a function that returns the minimum value of an array based on some criteria because it's simpler than a sort, but it still will serve fine as an illustration:

function min(collection, property) {
    if (collection.length == 0) {
        return null;
    }

    var minIndex = 0;
    var minValue = collection[0][property];
    for (var i = 1; i < collection.length; ++i) {
        if (minValue > collection[i][property]) {
            minValue = collection[i][property];
            minIndex = i;
        }
    }

    return collection[minIndex];
}

var items = [
    { name: "John", age: 20 }, { name: "Mary", age: 18 }
    ];

alert(min(items, 'age').name); // prints "Mary"

See this code run. What is the problem with it?

The problem is that while we made some effort in order to create a configurable min function (it finds the minimum based on any property we specify), it's still pretty limited in the general sense because it can only find the minimum based on one single property.

What if our data was like this?

var items = [ "John/20", "Mary/18" ];

This is no longer an array of objects, but an array of formatted strings. It has the same data as the original collections, but in a different structure. As a result, the original min function cannot be used to process it.

Now we could write another version of min that works with strings instead of objects, but that would be kind of pointless (there are infinitely many ways to represent the same data). This might be OK as a quick and dirty solution for one particular case, but consider the problem of a library writer: how could they write one function useful to everyone, without knowing how the data will be structured in each case?

Callbacks to the rescue

Simple: make the min function more powerful by allowing its user to specify how to extract the criteria from the data. This sounds very much like "giving the function code to extract the criteria from the data", which is what we are going to do. min will accept a callback:

function min(collection, callback) {
    if (collection.length == 0) {
        return null;
    }

    var minIndex = 0;
    var minValue = callback(collection[0]);
    for (var i = 1; i < collection.length; ++i) {
        if (minValue > callback(collection[i])) {
            minValue = callback(collection[i]);
            minIndex = i;
        }
    }

    return collection[minIndex];
}

var items = [ "John/20", "Mary/18" ];

var minItem = min(items, function(item) {
    return item.split('/')[1]; // get the "age" part
});

alert(minItem);

See this code run.

Now at last our code is reusable. We can easily configure it to work both with the first and the second type of data with minimal effort.

See the callback-enabled function handle both types of data. This is the result of the extreme flexibility we gained as a result of allowing the user to provide code to run as part of our function, in the form of a callback.

Callbacks in asynchronous programming

Another major use of callbacks is to enable asynchronous programming models. This is useful in all cases where you schedule an operation to complete, but do not want your program to stop running until the operation has completed.

In order for this to work, you provide the operation with a callback function and effectively tell it: go do this work for me, and when you are done, call me back. You are then free to go ahead and do anything you like, knowing that when the results are available the function you specified will run.

This is most visible in the web with AJAX: you fire off a request to a web server, and when the response has been received you act upon it in some manner. You don't want to wait on the spot for the response because it might take a lot of time (e.g. if there are connection problems) and you don't want your program to be unresponsive for all that time.

For an example of an AJAX callback, see the documentation for the jQuery load function.



回答2:

In JavaScript, a great deal of function calls are asynchronous (for instance using XMLHTTPRequest to perform AJAX requests). As such, you don't know when the return value will arrive, as your browser us executing something else in the background.

Without causing the rest of your script to stop running, the notion of a callback function allows you to run a procedure as soon as an asynchronous call has finished performing what it needs to do.

For instance, suppose I'm requesting foo.json from somewhere. I would do something like (fictional code):

ajaxGet("foo.json", function(data)
{
    doSomethingWith(data);
});
doSomethingElse();

The callback function will (function(data) { ... }) will execute of its own accord when ajaxGet has retrieved the data and is now ready. Before that, doSomethingElse() will execute and not hang the browser.


To answer your question regarding other JavaScript environments, let's look at node.js, arguably the most popular one there is.

Now, node.js is fully asynchronous, meaning that function calls never block (theoretically) and therefore you're going to have to have callbacks that call callbacks for performing any kind of asynchronous I/O. In fact, I would say node.js is almost bona fide continuation-passing style, where functions don't return and rely on callbacks to pass their values forward.



回答3:

Callbacks are highly popular in JavaScript since AJAX. AJAX is asynchonous call to another Web resouce, since you don't know when it is going to be finished, application don't need to wait for response. Instead, it continues exectution and you provide a callback to be called as soon as request is done.

This simple code provides callback, to show the products retrieved from server:

$.get('/service/products', function(p) {
   alert(p);
});

Moreover, due to JavaScript is functional language by nature functions have major role here. You are passing functions to functions, or returning functions from function. It is very useful for different patterns implementations, like

function getPersonFactory(name) {
   var create = function () {
      return new Person(name);
   }

   return create;
}

var factory = getPersonFactory('Joe');
var person = factory();

Because of JavaScript functional nature and goog-looking and easy callback programming it found its implications for frameworks like Node.js, which is javascript framework highly optimized on IO operations.

Node.js HelloWorld:

var http = require("http");

http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}).listen(8888);

See, it creates HTTP server and provide callback for each request comming to that server. You will out the response and ends it. Very compact and efficient code.

For hardcore functional programming you might take a look this great article:

http://matt.might.net/articles/implementation-of-recursive-fixed-point-y-combinator-in-javascript-for-memoization/



回答4:

Callbacks are inevitable if you do any kind of event listening. JavaScript and event-driven paradigm are brothers in arms. User interaction as presented in web browsers is the reason.

Whether you listen to DOM events, Ajax completion, file system access or HTTP requests in Node.js, you must define a procedure to be called upon event occurring.

More advanced form of callbacks are promises, for instance.


Also, callbacks are handy as iteration processors:

[ 1, 2, 3 ].forEach(function callback(val) { console.log(val); });

The following could replace all letters with their code-points:

'hey hop hello'.replace(/./, function callback(s) { return s.charCodeAt(0); });


回答5:

Helps for Asynchronous function calling. Rather than waiting some function completes you can call and pass the callback to an asynchronous function and continue the current execution. Once the function completes it execute the call back. Ajax uses callbacks. This explains how Ajax uses callbacks clearly.

Here are some similar threads.

Why use callback in JavaScript, what are its advantages?

How can I take advantage of callback functions for asynchronous XMLHttpRequest?