We know that the execution environment of JS language is “single thread”. The so-called “single thread” means that only one task can be completed at a time. The advantage of this mode is that it is relatively simple to implement and the execution environment is relatively simple. The disadvantage is that as long as one task takes a long time, subsequent tasks must wait in line, which will delay the execution of the entire program. To solve this problem, the JS language divides the execution mode of the task into two modes: Synchronous and Asynchronous.

So why is asynchrony important? How can asynchrony be used to effectively handle potentially blocking operations?

Why asynchrony?

Typically, programs are executed sequentially, with only one thing happening at a time. If a function depends on the result of another function, it has to wait for that function to finish before it can continue. From the user’s point of view, the program is complete.

As you probably know, the Javascript language is executed in a “single thread” environment.

Single-threaded means that you can only complete one task at a time. If there are multiple tasks, they must be queued, the first task completed, the next task executed, and so on.

The advantage of this mode is that the implementation is relatively simple, the execution environment is relatively simple; The disadvantage is that as long as one task takes a long time, subsequent tasks must wait in line, which will delay the execution of the entire program. A common browser non-response (suspended animation) is usually the result of a single piece of Javascript code running for so long (such as an infinite loop) that the entire page gets stuck in one place and no other task can be performed.

Such as

Mac users may experience the rotating rainbow cursor (often referred to as the beach ball) that the operating system uses to tell the user, “The program running now is waiting for something else to complete before it can continue running. You must be worried about what’s going on for so long.”

It’s a frustrating experience, underusing the computing power of a computer — especially in an era when multi-core cpus are ubiquitous, and there’s no point in sitting around and waiting, when you could just do other work on a different processor core while the computer notifies you when it completes a time-consuming task. That way you can do other work at the same time, and that’s where asynchronous programming comes in. The programming environment you’re using (in the case of Web development, a Web browser) is responsible for providing you with the API to run such tasks asynchronously.

1.

Asynchronous techniques are very useful, especially in Web programming. When a Web application in the browser does intensive computing and does not return control to the browser, the browser freezes. This is called blocking. The browser can no longer process the user’s input and perform other tasks until the Web application is handed back control of the processor.

Let’s look at some examples of blocking.

Example: simple – sync. HTML

<! DOCTYPEhtml>
<html>
<head>
    <meta charset="utf-8">
    <title>Simple synchronous JavaScript example</title>
</head>
<body>
    <button>Click me</button>
    <script>
        const btn = document.querySelector('button');
        btn.addEventListener('click'.() = > {
            let start = new Date(a);let end;
            for (let i = 0; i < 10000000; i++) {
                let date = new Date(a); end = date; }let time = end - start;
            console.log('Total time to calculate 10 million dates:' + time + 'ms');
            let pElem = document.createElement('p');
            pElem.textContent = 'Total time to calculate 10 million dates:' + time + 'ms';
            document.body.appendChild(pElem);
        });
    </script>
</body>
</html>
Copy the code

An event listener is added to the button, and when the button is clicked, it performs a very time-consuming task (counting 10 million dates and displaying the final elapsed time in the console), then adds a paragraph to the DOM.

To run this example, open the JavaScript Console and click the button-you’ll notice that the paragraph doesn’t appear on the page until the date has been computed and the final time is displayed on the console.

The effect is as follows:

The code is executed in the order of the source code, and the following code is executed only after the preceding code has finished running.

Note: This example is unrealistic: it doesn’t normally happen in real life, no one would calculate dates 10 million times, it just provides a very intuitive experience.

2. The synchronous

To understand what asynchronous JavaScript is, we should start by understanding synchronous JavaScript exactly.

A lot of what we learned was basically synchronous: run the code, and the browser returns the results as soon as possible. Let’s start with a simple example

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8">
    <title>Simple synchronous JavaScript example</title>
  </head>
  <body>
    <button>Click me</button>
    <script>
    const btn = document.querySelector('button');
    btn.addEventListener('click'.() = > {
      alert('You clicked me! ');

      let pElem = document.createElement('p');
      pElem.textContent = 'This is a newly-added paragraph.';
      document.body.appendChild(pElem);
    });
    </script>
  </body>
</html>
Copy the code

The effect is as follows:

This code, executed line by line:

  1. Get a

  2. When a button is clicked, add a click event listener:

    1. alert()A message appears.
    2. Once the alert ends, create a

      element.

    3. Assign a value to its text content.
    4. Finally, put the paragraph into the web page.

Each action is executed without anything else happening – the rendering of the page is paused. As mentioned in the previous article, JavaScript is single-threaded. Only one thing can be done at any time, only one main thread, and everything else blocks until the previous operation is complete.

So in the example above, after clicking the button, the paragraph will not be created until you click OK in the Alert message box, you can try it yourself

Note: This is important to remember, alert() is useful for demonstrating blocking, but in formal code it is a nightmare.

3. Solve

To solve this problem, the Javascript language divides the execution modes of tasks into two types: synchronous(Synchronous)And asynchronous(Asynchronous).

  • “Synchronous mode” is the mode mentioned before, the latter task wait for the end of the previous task, and then execute, the execution order of the program is consistent with the task order, synchronous; Asynchronous mode “is completely different, each task has one or more of the callback function (the callback), before the end of a task, not to perform a task, after but the callback function, after a task is before the end of a task before execution, so the program execution order the order is not consistent with the task, asynchronous.

  • “Asynchronous mode” is very important. On the browser side, long operations should be performed asynchronously to prevent the browser from becoming unresponsive. The best example of this is Ajax operations. On the server side, “asynchronous mode” is even the only mode, because the execution environment is single-threaded, and if you allow all HTTP requests to be executed synchronously, the server performance deteriorates dramatically and quickly becomes unresponsive.

To put it simply: synchronous execution in your code order, asynchronous execution out of code order, asynchronous execution is more efficient.

Several approaches to asynchronous programming

1. Callback function

Callbacks are the most basic method of asynchronous programming.

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.

Translation:

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

Here is an example of a callback function:

function doSomething(msg, callback){
    alert(msg);
    if(typeof callback == "function") 
    callback();
 } 
doSomething("Callback function".function(){
    alert("Anonymous function callback!");
 }); 
Copy the code

Let’s look at some classic callback code that I’m sure you’ve used:

◾ 1. Asynchronous request ruin correspondence:

$.get("/try/ajax/demo_test.php".function(data,status){
    alert("Data:" + data + "\n status:" + status);
});
Copy the code

◾ 2. Array traversal callback function

array1.forEach(element= > console.log(element));
Copy the code

, etc.

The advantages of callback functions are that they are simple, easy to understand, and deploy. The disadvantages are that they are not conducive to code reading and maintenance, that they are highly coupled between parts, that the process can be confusing, and that only one callback function can be specified per task.

The most fatal drawback of the Callback function is that it is easy to write Callback hell. Assuming multiple requests have dependencies, you might write code like this:

ajax(url, () = > {
    // Process logic
    ajax(url1, () = > {
        // Process logic
        ajax(url2, () = > {
            // Process logic})})})Copy the code

2. Event monitoring

Another approach is to adopt an event-driven model. The execution of tasks depends not on the order of the code, but on whether an event occurs.

The callback function that the event listens for:

element.addEventListener("click".function(){ 
	alert("Hello World!"); 
});
Copy the code

This line of code means that when an Element click event occurs, the function passed in is executed.

The advantages of this approach are that it is easy to understand, can bind multiple events, each event can specify multiple callback functions, and can “decouple”, which is good for modularity. The disadvantage is that the entire program has to be event-driven, and the flow becomes very unclear.

Publish/subscribe

The publish-subscribe model is a one-to-many dependency between objects. When the state of an object changes, all dependent objects are notified of the state change.

  • Subscriber registers the events he/she wants to Subscribe to the Event Channel.
  • When the Publisher publishes the Event to the dispatch center, that is, when the Event is triggered, it is up to the dispatch Center to register the processing code for the Fire Event subscribers to the dispatch center.

◾ example

For example, we like to read a public number of articles, but we do not know when to release a new article, or to regularly read; At this time, we can pay attention to the public number, when there is an article push, there will be a timely message to inform us of the update of the article.

The above seemingly simple operation, in fact, is a typical release and subscription model, the public number belongs to the publisher, the user belongs to the subscriber; The user will register the event of subscribes to the public account with the dispatch center. The public account acts as the publisher. When a new article is published, the public account will publish the event to the dispatch center, and the dispatch center will send messages to inform the user in time.

◾ publish/subscribe has the advantage of decoupling between objects. In asynchronous programming, more loosely coupled code can be written. The downside is that creating a subscriber itself takes some time and memory, and while it can weaken the connection between objects, it is difficult to keep track of multiple publishers and subscribers nested together.

For those who want to implement publish/subscribe by hand, check out this post:From zero strap you hand write a “publish-subscriber model”, nanny level teaching

4. Promise

Promises are a way to handle asynchronous code without falling into callback hell.

Promise has been part of the language for years (standardized and introduced in ES2015) and has recently become more integrated with async and await in ES2017.

Asynchronous functions use promises underneath, so understanding how promises work is fundamental to understanding async and await.

The Promise object represents an asynchronous operation with three states: Pending, fulfilled and Rejected.

A Promise must be in one of the following states:

  • To be determined(pending): Initial state, neither honored nor rejected.
  • Has been successfully(fulfilled): indicates that the operation completed successfully.
  • Has refused to(rejected): Indicates that the operation fails.

When a promise is invoked, it starts as a pending state. This means that the called function continues to execute, and the promise remains in processing until resolved, providing whatever data is requested for the called function.

The promise that is created will eventually end with a state of fulfilled or rejected, and when it is fulfilled, the corresponding callback functions (passed to then and catch) will be called.

● The chain call of Promise

Promise instances have THEN methods, that is, then methods defined on the prototype object Promise.Prototype. It adds a callback function to the Promise instance when the state changes. As mentioned earlier, the first argument to the THEN method is the callback in the Resolved state and the second argument (optional) is the callback in the Rejected state.

The then method returns a new Promise instance (note, not the original Promise instance). So you can write it chained, where a then method is followed by another THEN method.

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});
Copy the code

The code above specifies two callback functions in turn, using the THEN method. After the first callback completes, the second callback is passed the result as an argument.

Using chained THEN, you can specify a set of callback functions that are called in order. In this case, the previous callback may still return a Promise object (with asynchronous operations), and the latter callback will wait for the state of the Promise object to change before it is invoked.

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function (comments) {
  console.log("resolved: ", comments);
}, function (err){
  console.log("rejected: ", err);
});
Copy the code

In the code above, the callback specified by the first then method returns another Promise object. At this point, the callback specified by the second THEN method waits for the new Promise object state to change. Call the first callback if it becomes Resolved, and the second if the status changes to Rejected.

The above code can be written more succinctly if the arrow function is used.

getJSON("/post/1.json").then(
  post= > getJSON(post.commentURL)
).then(
  comments= > console.log("resolved: ", comments),
  err= > console.log("rejected: ", err)
);
Copy the code

If you want a more detailed study Promise, you can refer to these articles:

  • Make sure you fully understand promises.
  • Promises/A+ 872 Official Test Cases
  • Promise all API tutorials, including promise.any () in TC39 phase 4 draft

5. async/await

The async and await keywords were recently added to the JavaScript language. They are part of ECMAScript 2017. Simply put, they are syntax-candy based on Promises that make asynchronous code easier to write and read. Using them, asynchronous code looks more like old-fashioned synchronous code, so they’re well worth learning.

If you want to learn more about async/await, please refer to this article I posted:

  • The user manual of ASYNc /await is the ultimate asynchronous programming solution for JS

More full more detailed quality content, click here to view

reference

  • Elegant asynchronous processing
  • Four approaches to Asynchronous programming in Javascript
  • Callback functions in JavaScript
  • JS asynchronous programming six schemes
  • JavaScript publish-subscribe mode
  • The core design pattern that the Web front end must master: the publish subscriber pattern
  • Async and await