Asynchronous Trilogy Callbacks

Overview

This is my reading notes for JavaScript (Volume 2) that you don't know , for reference in future development, and I believe it will be useful to others as well.

Asynchronous mechanism

Chunked program : part of the code we write is {running now} and the rest is {running in the future}.

We don't write them separately, because they are related. For example, {future running code} needs some variables of {now running code}, so how to make these variables still exist after {now running code} is finished running and Can be called by {future running code}? The answer is simple, closures , we put {code to run in the future} in a function scope, enabling it to use variables from the outer scope, and these variables persist even if the outer scope is destroyed. The function that generates this closure is called the callback function .

In the code we write, there may be more than one place that needs to be run in the future. Generally speaking, after the main thread of js finishes running the {now running code}, it continues to run {the future running code 1}, and then continues to run { code to run in the future 2}. . . So when running {code to run now}, {code to run in the future 1}, {code to run in the future 2}. . . Where is the code to run in the future? The answer is to put it in a queue, which is called a task queue .

Therefore, after the main thread finishes running {now running code}, it will take out {future running code 1} in the task queue to run, and after running, continue to take out { future running code 2} in the task queue to run. . . This mechanism in which the main thread keeps taking out the code in the task queue to run is called the event loop .

It should be noted that there may be such a situation, when running {code to run in the future 1}, another {code to run in the future x} is found. At this time, a task queue 2 will be re-created , and {running in the future will be set. The code x} is stuffed into it, and the code in task queue 2 is run after the code in the previous task queue has finished running .

The second point to note is that the running order of {future running code 1}, {future running code 2},,,{future running code x} is not necessarily the first-in-first-out order in the queue, usually the case Yes, each will run after certain conditions are met, such as how many seconds later, or after receiving a certain data.

The third point to note is that before es6, this task queue was not created by js, but implemented by the browser. It is generally used to run asynchronous operations such as settimeout and ajax.

In es6, js standardizes the task queue , which is called microtask, and the previous task queue is called macrotask. Microtask is used to run asynchronous operations such as promises, and it runs before the macrotask in the same event loop.

Concurrency

Due to the asynchronous mechanism of js, js can appear to be processing multiple tasks at the same time when it is running. This simultaneous occurrence is called concurrency . There is another mechanism to achieve concurrency, that is, multiple processes or threads run at the same time. This situation where multiple processes or threads run at the same time is called parallelism .

There is a very important situation in concurrency, that is, the execution order of the code to be executed in the future will affect the final result. The example is as follows. The difference in the execution order of block 2 and block 3 will cause the final value of a and b to be different. This uncertainty about the order in which the code runs is called a race condition.

//块 1:
var a = 1;
var b = 2;
//块 2( foo() ):
a++;
b = b * a;
a = b + 3;
//块 3( bar() ):
b--;
a = 8 + b;
b = a * 2;

A very realistic example of an asynchronous race condition is as follows:

var a, b;
function foo(x) {
    a = x * 2;
    baz();
}
function bar(y) {
    b = y * 2;
    baz();
}
function baz() {
    console.log(a + b);
}
// ajax(..)是某个库中的某个Ajax函数
ajax( "http://some.url.1", foo );
ajax( "http://some.url.2", bar );

How to handle this race condition? The method is to add a judgment (so judgment is very commonly used in asynchronous).

var a, b;
function foo(x) {
    a = x * 2;
    if (a && b) {
        baz();
    }
}
function bar(y) {
    b = y * 2;
    if (a && b) {
        baz();
    }
}
function baz() {
    console.log( a + b );
}
// ajax(..)是某个库中的某个Ajax函数
ajax( "http://some.url.1", foo );
ajax( "http://some.url.2", bar );

This kind of need for two asynchronous completions at the same time is called a gate. The other is that we only need the asynchronous data that is completed first. This situation is called a latch. The example is as follows:

var a;
function foo(x) {
    if (!a) {
        a = x * 2;
        baz();
    }
}
function bar(x) {
    if (!a) {
        a = x / 2;
        baz();
    }
}
function baz() {
    console.log( a );
}
// ajax(..)是某个库中的某个Ajax函数
ajax( "http://some.url.1", foo );
ajax( "http://some.url.2", bar );

Callback

As we said above, we generally wrap the code to be executed in the future in a callback function, and execute it after a certain condition is met, such as the following code:

listen( "click", function handler(evt){
    setTimeout( function request(){
        ajax( "http://some.url.1", function response(text){
            if (text == "hello") {
                handler();
            }
            else if (text == "world") {
                request();
            }
        } );
    }, 500) ;
} )

At first glance, the callback function seems to be very clear, but this is only superficial. Let's look at the following pseudo-code, where doABCDEF is an asynchronous function.

doA( function(){
    doB();
    doC( function(){
        doD();
    } )
    doE();
} );
doF();

The actual running order is not ABDEF, but AFBCED. When there are more nests, it will be more complicated, and it takes a long time to know the execution order. This is the famous callback hell. (Note that callback hell doesn't mean too much nesting is inconvenient to write due to indentation, but that it's less readable after too much nesting.)

trust issues

Callback hell is only part of the callback problem, there are some deeper issues to consider. For example the following example:

// A
ajax( "..", function(..){
    // C
} );
// B

After executing A and B we will execute the asynchronous code block C. That is, now the asynchronous code block C has full control of the program and can control all variables and methods in scope.

At this time, we have reason to worry:

  1. What if the async code block C doesn't execute at all?
  2. What should I do if the variables required by the asynchronous code block C call are also asynchronous? (call prematurely)
  3. What to do when an asynchronous code block C call is too late?
  4. What should I do if the ajax data obtained by the asynchronous code block C does not conform to the specification?
  5. What if the execution time of the asynchronous code block C is too long? Possible permanent execution?
  6. What if the error is swallowed?

More generally, the block C of code we sometimes execute is a third-party function that we can't see. At this time, since the code block C can call all variables and methods in the scope, what if the third-party function arbitrarily changes these variables?

The above is the trust problem brought by the callback function. The root cause is that we hand over control to the callback function C.

Of course, there are remedies for the above problems, but dealing with all of them is still very cumbersome. Careful people may see that some of the above problems are caused by simultaneous asynchronous and synchronous, and this also leads to a very effective advice: always call the callback asynchronously, even on the next round of the event loop. For example the following code:

function result(data) {
    console.log( a );
}
var a = 0;
ajax( "..pre-cached-url..", result );
a++;

If ajax obtains data faster than console.log IO port read and write (cache storage), it will print 0, otherwise it will print 1.

It should be noted that even if we encounter so many problems in the callback function, in small projects, we will actually encounter many fewer problems, so it is safe to use callbacks.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324551093&siteId=291194637