js synchronous and asynchronous

Posted by EWaveDev on Thu, 27 Jan 2022 20:09:11 +0100

@

① Asynchronous API

Synchronization API: synchronization tasks are executed on the main thread to form an execution stack. The next API can only be executed after the execution of the current API is completed

console.log('before');
console.log('after');
/*Output results
*,

Asynchronous API: the execution of the current API will not block the execution of subsequent code
The asynchrony of JS is realized through callback function.

Callback function: define your own function and let others call it

// getData function definition
function getData (callback) {}
// getData function call
getData (() => {});

Generally speaking, there are three types of asynchronous tasks:
1. Event functions, such as click, resize, etc
2. Resource loading, such as load, error, etc
3. Timer, including setInterval, setTimeout, etc
4,ajax
Asynchronous task related callback functions are added to the task queue (also known as message queue)

console.log('before');
//Asynchronous API s do not block the execution of subsequent code
setTimeout(() => {console.log('last');}, 2000);
console.log('after');
/*
Output result:
before 
after
last
*/


② js execution mechanism

The execution environment of JavaScript is "single thread"
The so-called single thread means that there is only one thread in the JS engine responsible for interpreting and executing JavaScript code, that is, it can only complete one task at a time. After this task is executed, it can execute the next one. It will "block" other tasks. This task can be called the main thread
Asynchronous mode can perform multiple tasks together

  1. First execute the synchronization task in the stack.
  2. The asynchronous task (callback function) is put into the task queue.
  3. Once all the synchronous tasks in the execution stack are executed, the system will read the asynchronous tasks in the task queue in order, so the read asynchronous tasks end the waiting state, enter the execution stack and start execution.

Because the main thread repeatedly obtains, executes, re obtains and re executes tasks, this mechanism is called event loop.

console.log(1);
document.onclick = function() {
console.log('click');
}
console.log(2);
setTimeout(function() {
console.log(3)
}, 3000)

Console output results

1
2
//After clicking the screen
click
//After 3 seconds
3
//After clicking the screen
click

Due to this mechanism, there will be some problems when asynchrony and synchronization work together, such as:

  1. The synchronous API cannot get the result of asynchronous API execution directly from the return value
    // asynchronous
    function getMsg(callback) {
        setTimeout(function() {
            callback({
                msg: 'hello node.js'
            })
        }, 2000)
    }
    let msg = null;
    getMsg(function(data) {
        //console.log(data);
        msg = data;
        console.log(msg);
    });
    console.log(msg);
    
    /*Execute output
    null
    { msg: 'hello node.js' }
    */
    
  2. The synchronous API executes from top to bottom. The previous code will block the execution of the following code. The asynchronous API will not wait for the API execution to complete before executing the code downward. The callback function is used to obtain the execution result of the asynchronous API
    console.log('The code starts executing');
    setTimeout(() => {
    	console.log('2 Code executed in seconds');
    }, 2000);
    setTimeout(() => {
    	console.log('"0 second"Code executed after');
    }, 0);
    console.log('End of code execution');
    


③ Callback hell

Sometimes, in order to ensure the execution order, you have to continue the asynchronous operation in the callback of the asynchronous operation. Such continuous nesting will form a callback hell, such as:

  • Asynchronous request of ajax
    $.ajax({
      url: 'http://localhost:3000/data',
      success: function(data) {
        console.log(data)
        $.ajax({
          url: 'http://localhost:3000/data1',
          success: function(data) {
            console.log(data)
            $.ajax({
              url: 'http://localhost:3000/data2',
              success: function(data) {
                console.log(data)
              }
            });
          }
        });
      }
    });
  • File reading in nodeJs

Question: if the execution of the code behind the asynchronous API depends on the execution result of the current asynchronous API, but the asynchronous API has not returned the result when the subsequent code is executed, how to solve this problem?

// File read as asynchronous operation
fs.readFile('./demo.txt', (err, result) => {});
console.log('File reading results');

Requirements: read A file, B file and C file in sequence

const fs = require('fs');

fs.readFile('./1.txt', 'utf8', (err, result1) => {
	console.log(result1)
	fs.readFile('./2.txt', 'utf8', (err, result2) => {
		console.log(result2)
		fs.readFile('./3.txt', 'utf8', (err, result3) => {
			console.log(result3)
		})
	})
});

Callback Hell: callback nested callback, although it can meet the above requirements, the readability of the code is poor and difficult to maintain



④ Promise (ES6)

Promise overview

Promise is a solution to asynchronous programming. Grammatically speaking, promise is an object from which you can get messages of asynchronous operations.

Promise has the following advantages:
⚫ It can avoid the nesting problem of multi-layer asynchronous calls (callback hell)
⚫ Promise object provides a concise API, which makes it easier to control asynchronous operations

Promise - MDN

Promise basic usage

  • Instantiate Promise object and transfer function in constructor, which is used to handle asynchronous tasks
  • The resolve and reject parameters are used to handle success and failure, obtain the processing results through p.then, and synchronize the asynchronous API through Promise
var p = new Promise(function(resolve, reject){
  // This is used to implement asynchronous tasks
  setTimeout(function(){
    var flag = false;
    if(flag) {
      // Called on success
      resolve('hello');
    }else{
      // Called on failure
      reject('Error ');
    }
  }, 100);
});
p.then(function(data){
	// Get normal results from resolve
  console.log(data)
},function(info){
	// Get error message from reject
  console.log(info)
});

Handling Ajax requests based on Promise

The chain call of then can be used to process multiple ajax requests, such as:

 function queryData(url) {
   var p = new Promise(function(resolve, reject){
     var xhr = new XMLHttpRequest();
     xhr.onreadystatechange = function(){
       if(xhr.readyState != 4) return;
       if(xhr.readyState == 4 && xhr.status == 200) {
         // Handle normal conditions
         resolve(xhr.responseText);
       }else{
         // Handling exceptions
         reject('Server error');
       }
     };
     xhr.open('get', url);
     xhr.send(null);
   });
   return p;
 }
 queryData('http://localhost:3000/data')
      .then(function(data){
        console.log(data) // Hello World!
        /* 
        In the then method, you can directly return normal data or Promise objects,
        If the Promise object is returned as the caller of the following then(data), data is used to receive the asynchronous execution result in the object
        Ordinary data can also be received by the data in the following then(data)
        */
        return queryData('http://localhost:3000/data1');
      })
      .then(function(data){
        console.log(data); // Output Hello TOM!
        return queryData('http://localhost:3000/data2');
      })
      .then(function(data){
        console.log(data) // Output Hello JERRY!
        return 'abc'
      })
	  .then(function(data){
		console.log(data) // Output abc
	  })	
;

Promise basic API

Example method

method describe
p.then() Get the correct result returned by the asynchronous task resolve()
p.catch() Get the exception information returned by asynchronous task reject()
p.finally() Asynchronous tasks are executed whether they are successful or not (not yet a formal standard)

Note: can p.then() get both correct and wrong results asynchronously

function foo() {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            // resolve(123);
            reject('error');
        }, 100);
    })
}
foo()
    .then(function (data) {
        console.log(data)
    })
    .catch(function (data) {
        console.log(data)
    })
    .finally(function () {
        console.log('finished')
    });
// Equivalent writing    
// foo()
//     .then(function(data) {
//         console.log(data)
//     }, function(data) {
//         console.log(data)
//     })
//     .finally(function() {
//         console.log('finished')
//     });

Object method

method describe
Promise.all() Multiple asynchronous tasks are processed concurrently, and the results can be obtained only after all tasks are completed
Promise.race() Concurrent processing of multiple asynchronous tasks, as long as one task is completed, the results can be obtained
  • Promise. The all method accepts an array as a parameter, and the objects in the array (p1, p2, p3) are promise instances (if it is not a promise, this item will be converted to a promise with Promise.resolve). Its state is determined by these three promise instances
  • Promise. The race method also accepts an array as an argument. When the state of one instance in p1, p2 and p3 changes (becomes fully or rejected), the state of p changes accordingly. And pass the return value of promise that changes the state first to the callback function of p
// Call the above queryData() to obtain multiple promise objects that wrap asynchronous tasks
var p1 = queryData('http://localhost:3000/a1');
var p2 = queryData('http://localhost:3000/a2');
var p3 = queryData('http://localhost:3000/a3');
// Process through all()
Promise.all([p1,p2,p3]).then(function(result){
  console.log(result); 
  // Output an array in which each element is the execution result of asynchronous task (3) ['Hello Tom! ','Hello Jerry!','Hello spike! ']
})
// Process through race()
Promise.race([p1, p2, p3]).then(function(result) {
    console.log(result) // Output the result of the execution of the first asynchronous task: Hello TOM! 
})


⑤ Asynchronous function

Asynchronous function is the ultimate solution of asynchronous programming syntax. It allows us to write asynchronous code in the form of synchronization, so that the code no longer has callback function nesting, making the code clear

Asynchronous functions are inseparable from async and await keywords. async/await is a new syntax introduced by ES7, which can make asynchronous operation more convenient

async keyword

  1. Add the async keyword before the definition of ordinary functions. Ordinary functions become asynchronous functions
  2. Use the return keyword inside the asynchronous function to return the result. The result will be wrapped in the promise object. The return keyword replaces the resolve method
  3. Throw keyword is used inside asynchronous function to throw program exception, and throw keyword replaces reject method
  4. The asynchronous function returns the promise object containing the returned result by default through return or throw
  5. Call the asynchronous function and then chain call then() to get the result of the asynchronous function return
  6. Call the asynchronous function and then chain call catch() to get the error information of the asynchronous function throw

await keyword

  1. The await keyword can only appear in asynchronous functions
  2. await promise. Only promise objects can be written after await. It is not allowed to write other types of API s
  3. The await keyword suspends the asynchronous function from executing downward until promise returns a result
async function queryData() {
  var ret = await new Promise(function(resolve, reject){
    setTimeout(function(){
      resolve('nihao')
      // resolve('nihao1')
    },1000);
  })
  // console.dir(ret)
  if(ret == 'nihao'){
    return ret; //Promise { 'nihao' }
  }else {
    throw 'error!' // Promise { 'error!' }
  }
}
queryData().then(function(data){
  console.log(data)
}).catch(function(err){
  console.log(err);
})

Topics: Javascript