preface

Callback is a callback function. But you probably don’t understand the concept very well. For example, with Ajax, you only know how to call return functions, and if you don’t understand callback clearly, you will probably crash after learning Node.js, because callback is one of the three core components of Node.js.

The callback function

The concept of a callback function

A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.

A callback is a call to a function. So start by understanding the call process. Function A takes one argument, and that argument is function B, which is executed when function A is done. This process is called a callback.

In fact, Chinese is also very easy to understand: callback, callback, is back to call the meaning. Function A is done first, and function B is called later.

Here is an example of a callback

Function a(callback) {alert(" I am parent function a! ); Alert (" call callback function "); callback(); } function b(){alert(" I am a callback function b"); } function c(){alert(" I am calling c"); } function test() { a(b); a(c); }Copy the code

From the above example, we can see that the callback is not directly related to synchronous and asynchronous, callback is just a way of implementation, there can be synchronous callback, there can be asynchronous callback, there can be event processing callback and delay function callback, these are used in many scenarios in our work.

Why write a callback function

1. About callback function and JS single thread and JS asynchronous mechanism

We all know that js is single-threaded, this design pattern brought us a lot of convenience, we don’t need to worry about the communication between each thread, also do not need to write a lot of burn brain code, that is to say, js engine can only be a one thing to do and perform related operations, so all things need to perform like a queue, waiting to be triggered and execution, But if so, if in the queue have one thing need to spend a lot of time, so the back of the task will be in a wait state, sometimes even appear browser feign death phenomenon, such as the one thing that is executing a task is an infinite loop, and then leads to other tasks to perform follow-up, Therefore, JS designs asynchronous mode under the defect of synchronous mechanism

In asynchronous execution mode, each an asynchronous task has its own one or more callback function, so that the current in the implementation of the asynchronous task execution, not immediately implement the next task in the event queue, but perform its callback function, and the current such as the next task, also won’t this callback function performed, Because it is also not certain that the current callback is properly executed, it will execute whenever it is triggered, for example,

Image resources have not been requested

Above you can see, I want to buy a thing, when I point into the details of the item page, image resources haven’t request to complete, but now I can click on the add to cart, launched another request, js task list to add a shopping cart in the event will start execution, it also won’t interfere with the request of the image resources mission, This is the beauty of asynchronous execution

2. Js single threaded browser kernel multithreading

When it comes to js single thread, by the way, I want to know about the multi-threading of the browser kernel. I will not explain the working principle of the browser here, because I am not thorough in my research, and I will wait for the study and study to share it thoroughly

Cayley sketches

Browsers live in three main threads: THE JS engine thread, the GUI rendering thread, and the browser event triggering thread

See the figure you will be suddenly enlightened many, because the browser is a multithreaded execution environment, the multiple threads in the browser kernel distribution, one of the main thread is the thread of js engine, asynchronous request of js event queue at the same time, interactive event triggers, timer event is triggered by an event browser thread for listening, When the browser’s event-triggering thread is triggered, it adds the task to the JS engine’s task queue and starts executing the task when the JS engine is idle

The this in the callback refers to the problem

Using the js timer (setInterval, setTimeout), it is easy to encounter this pointing problem.

Go straight to the example:

 1 var name = 'my name is window';
 2 var obj = {
 3     name: 'my name is obj',
 4     fn: function () {
 5         var timer = null;
 6         clearInterval(timer);
 7         timer = setInterval(function () {
 8             console.log(this.name);  //my name is window
 9         }, 1000)
10     }
11 }
Copy the code

In this case, we can see from this.name that this refers to window.

If there is no special reference, both setInterval and setTimeout callbacks refer to this as window. This is because the JS timer method is defined under the window. However, in many scenarios, the direction of this needs to be modified. Here are a few:

1. The most common method: store this as a variable in an external function and use it in a callback instead of using this directly.

 1 var name = 'my name is window';
 2 var obj = {
 3     name: 'my name is obj',
 4     fn: function () {
 5         var that = this;
 6         var timer = null;
 7         clearInterval(timer);
 8         timer = setInterval(function () {
 9             console.log(that.name);   //my name is obj
10         }, 1000)
11     }
12 }
Copy the code

Var that = this; Use that instead of this in the callback function. This method is the most common and widely used.

2, use bind() method (bind() is the standard of ES5, there are compatibility problems under the earlier version of IE, you can introduce es5-shim.js to solve the problem)

Bind () works like call and apply in that it modifies the this reference. Call and apply, however, modify this and the function executes immediately, while bind returns a new function that creates a new function with the same body as the original function, with this pointing to the object passed in.

 1 var name = 'my name is window';
 2 var obj = {
 3     name: 'my name is obj',
 4     fn: function () {
 5         var timer = null;
 6         clearInterval(timer);
 7         timer = setInterval(function () {
 8             console.log(this.name);   //my name is obj
 9         }.bind(this), 1000)
10     }
11 }
Copy the code

The reason why call and apply are not used here is that call and Apply do not return functions, but execute functions immediately, and thus lose their function as timers.

3. Use the arrow function in ES6: the best use of the arrow function is that this points to.

 1 var name = 'my name is window';
 2 var obj = {
 3     name: 'my name is obj',
 4     fn: function () {
 5         var timer = null;
 6         clearInterval(timer);
 7         timer = setInterval(() => {
 8             console.log(this.name);  //my name is obj
 9         }, 1000)
10     }
11 }
Copy the code

The arrow function does not have its own this; its this inherits from the scope of the outer function. So, in this case, the timer callback function this inherits fn’s this. Of course, arrow functions also have compatibility problems. If they are compatible with lower versions of IE, they need to compile using Babel and introduce ES5-shim.js.

Callback functions are useful for asynchronous programming

In the running of a program, when the process of some requests is too long to wait for the completion of the request to proceed to the next task, asynchronous processing using callback functions can greatly improve the efficiency of the program execution. For example, AJAX requests. If the callback function is used for processing, the code can move on to other tasks without waiting. In real development, asynchronous calls are often used in javascript! Here is an example of loading an XML file using AJAX and using the call() function to invoke the callback function in the context of the requested Object.

Function fn(url, callback){var httpRequest; // create XHR httpRequest = window.xmlhttprequest? New XMLHttpRequest() :// Check the functionality of IE window.activexObject? new ActiveXObject("Microsoft.XMLHTTP") : undefined; HttpRequest. Onreadystatechange = function () {if (httpRequest. Readystate = = = 4 && httpRequest. Status = = = 200) {/ / state callback.call(httpRequest.responseXML); }}; httpRequest.open("GET", url); httpRequest.send(); } fn("text.xml", function(){// call console.log(this); }); Console. log(" This will run before the above callback."); // This statement is printed firstCopy the code

We request asynchronous processing, which means that when we start the request, we tell them to call our function when they’re done. In reality, the onReadyStatechange event handler also has to take into account the request failure, assuming that the XML file exists and can be loaded successfully by the browser. In this example, the asynchronous function is assigned to the onReadyStatechange event and therefore will not be executed immediately. Finally, the second console.log statement is executed first because the callback function is not executed until the request completes.

Callbacks are often used in Javascript programming in several ways, especially in modern Web application development and libraries and frameworks:

  • Asynchronous calls (e.g. reading files, making HTTP requests, loading JS files dynamically, performing callbacks after loading iframe resources, images loaded, etc.)
  • Event listener/handler
  • SetTimeout and setInterval methods
  • General: Lean code

5. “Callback hell” problem and solution

I have never encountered so many nested callbacks. The following content is directly from the Internet copy, we look, or very good understanding. When executing asynchronous code, no matter what order the code is simply executed, it is often the case that so many levels of callbacks pile up that the code looks like this. This jumble of code is called callback hell because there are so many callbacks that it’s hard to read the code. I took an example from Node-mongodb-native, a mongodb driver for Node.js. The code below illustrates callback hell:

Var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {' PK ':CustomPKFactory}); p_client.open(function(err, p_client) { p_client.dropDatabase(function(err, done) { p_client.createCollection('test_custom_key', function(err, collection) { collection.insert({'a':1}, function(err, docs) { collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) { cursor.toArray(function(err, items) { test.assertEquals(1, items.length); // Let's close the db p_client.close(); }); }); }); }); }); });Copy the code

You don’t want to run into this problem in your code, and when you run into it — and you will run into it — there are two solutions to this problem.

Give your functions names and pass their names as callbacks, instead of defining anonymous functions as arguments to the main function. Modularity L separates your code into modules, so that you can place pieces of code around to do specific work. You can then import modules into your giant application