JS asynchronous: execution principle and callback

JS asynchronous: execution principle and callback



1. The principle of JS asynchronous execution

  We know that JavaScript is single-threaded , while browsers are multi-threaded . Single-threaded execution tasks need to be queued one by one. If a task takes a long time to execute (like ajax takes a long time), it will directly cause no response, and the following tasks have been waiting for execution. At this time, you need to use asynchrony.

  To understand asynchrony, we must first know that browsers have three basic resident threads: JS engine thread , event trigger thread , and GUI rendering thread .

  The JS engine thread and the event trigger thread together constitute an event loop mechanism , and the GUI rendering thread and the JS engine are mutually exclusive. When the JS engine is executed, the GUI thread will be suspended, and the GUI updates are stored in a queue. When the JS engine is idle, it is executed immediately.

  We analyze from its event loop mechanism: Insert picture description here
  JS engine threads are divided into synchronous and asynchronous tasks:

    1. All synchronization tasks are executed through the main thread to form an execution stack .
    2. When there is an asynchronous task, hand it over to the asynchronous process (WebAPIs): Including event trigger thread or timer thread processing to form a task queue .
    3. When all tasks in the execution stack are processed and the main thread is idle, tasks will be extracted from the task queue to the execution stack for execution.

  In layman's terms, JavaScript also has a task queue in addition to the main thread. The task queue stores content that needs to be executed asynchronously. After the main thread is executed, it will continue to scan and execute the tasks in the task queue until the queue is emptied.

Draw solution:


Insert picture description here

  As shown in the picture, Xiaoming will be unable to play the DNF game if it takes a long time to learn, so he puts the learning in the asynchronous task queue, and then learns (task queue) after playing the game (main thread). During the period, the mother added learning events (DOM events), and Xiaoming would check what other tasks were (cyclic scanning) every time Xiaoming completed a learning task, until the final completion.


  Let's look at another example (the browser refreshes and keeps clicking the button):

      let myData = null
      //ajax请求
      function ajax() {
    
    
      //腾讯新冠实时数据接口,仅做学习
        axios.get('https://api.inews.qq.com/newsqa/v1/query/inner/publish/modules/list?modules=chinaDayList,chinaDayAddList,nowConfirmStatis,provinceCompare')
          .then(data => {
    
    
            console.log("ajax返回成功");
            myData = data.data
            console.log(myData);

          })
          .catch(error => {
    
    
            console.log("ajax返回失败");
          })
      }
      console.log(myData);
      ajax()
      setTimeout(() => {
    
    
        console.log('定时器');
      }, 2000);
      console.log(myData);
      const btn = document.querySelector('button')
      btn.onclick = () => {
    
    
        console.log("点击了");
      }

null
null
ajax returns success
Object
clicked
Timer
clicked

  It can be seen that the console is executed synchronously in the main thread and executed first, while the task queue outside the main thread stores the content of asynchronous execution. Here are setTimeout, ajax and DOM events, which are executed in the order of the task queue (cyclic scanning Queue) .
  
  Why do you want to scan in a loop?

  It can be seen from the click event that when the user interacts (click event, scroll event, window size change event, etc.), new events will be added to the task queue in the event loop, and then wait for execution, so cyclic scanning is required.

Two, callbacks in JS asynchronous


  Since asynchrony is executed in the last task queue, a lot of our logic is difficult to implement. At this time, we need to deal with this asynchronous logic. The most common way is callback -call back.

Callback function: Simply put, when function B is passed in as a parameter in function A, function B is the callback function executed by function A. There are two types of callbacks: nested callbacks and chained callbacks.


  Here is a simple usage of callback:

      let myData = null
      console.log(myData);
      setTimeout(() => {
    
    
        console.log('定时器');
      }, 2000);
      const btn = document.querySelector('button')
      btn.onclick = () => {
    
    
        console.log("点击了");
      }
      let name = "张三"
      function hr(callback) {
    
    
        setTimeout(() => {
    
    
          console.log(`我是${
      
      name}`);
          callback();
        }, 2001);
      }
      console.log(myData);
      function gj() {
    
    
        console.log(`${
      
      name}你好,我是李四,认识一下吧`);
      }
      hr(gj)

null
null
clicked
timer
I’m Zhang
San Zhang San Hello, I’m Li Si, let’s get to know you
clicked

  Obviously, we use callbacks when our functions need data, and asynchronous callbacks are used here.

  Although callback is a common method to solve asynchronous, it is accompanied by the increasingly complex needs of JS. Synchronous and asynchronous require more and more callback implementation logic. The mixing of synchronous and asynchronous and excessive callback nesting and indentation make the code difficult to interpret and maintain, forming "callback hell" .

Insert picture description here

  Let's look at an example:

const verifyUser = function(username, password, callback){
    
    
   dataBase.verifyUser(username, password, (error, userInfo) => {
    
    
       if (error) {
    
    
           callback(error)
       }else{
    
    
           dataBase.getRoles(username, (error, roles) => {
    
    
               if (error){
    
    
                   callback(error)
               }else {
    
    
                   dataBase.logAccess(username, (error) => {
    
    
                       if (error){
    
    
                           callback(error);
                       }else{
    
    
                           callback(null, userInfo, roles);
                       }
                   })
               }
           })
       }
   })
};

Most people just see the above code and feel the taste of their brain freezing. If there are hundreds of such code blocks in a project, after a period of time, I believe that even the people who wrote it will have a headache. Coming to your own project is like coming to hell.

  The most important thing is that at the same time, the callback still has a trust problem , and he gives the execution control to a third party (such as ajax). In order to solve the trust problem, we must write various logics in the program to solve the trust problem caused by callbacks.
  ·The call is too early
  ·The call is over
  ·The number of calls is too many or too few, and the required parameters are not successfully passed to the callback function.
  ·The possible errors are swallowed.

  It can be found that writing specific logic to solve specific trust issues has made it more difficult than its own application value, and it will also cause problems such as code redundancy and poor readability.


  To sum up: Callbacks solve the defects of asynchronous:
     1) It does not conform to the logical thinking of people's task processing
     2) The trust problem caused by callbacks.


  Faced with the increasingly obvious drawbacks of callbacks, ES6 has updated Promises to solve asynchronous problems. The next article is ES6-Promise.

Guess you like

Origin blog.csdn.net/Zxinxxx/article/details/114444890