Asynchronous development

Why asynchrony?

First let’s take a quick look at the concepts of synchronization and asynchrony

Synchronization: Once the invocation begins, the caller must wait until the calling method returns before continuing with the subsequent behavior. The caller actively waits for the result of the call.

Asynchronous: When an asynchronous call is made, the call is returned immediately and the caller does not get the result immediately. Instead, the caller is notified by some notification, or the call is handled by a callback function.

I recommend an article about synchronization and asynchrony for those who are interested.

Let’s imagine a scenario in which your business needs to get HTTP requests from the server. At this point you make a request to the server using Ajax.

 $.get(url, function(data) {console.log(data) });

If the request is executed synchronously, then you need to wait for the Ajax response. However, in the network request, the speed of the request will fluctuate greatly. Maybe the server of the other party will hang up, which will lead to the failure of the response all the time, so we can’t continue the follow-up work. So to be productive, we want when we make a request, we only care about processing the data after the response, and while we wait for that response, we do something else. So, we need asynchrony to be efficient


Javascript asynchronous development experience

So, we know that in most operations to get resources, we need asynchronous operations. So how did javascript asynchrony evolve step by step?

1. Callback function

We can operate asynchronously through callback functions.

Example: Read a file asynchronously

ReadFile ('./1. TXT ','utf8',function(err,data){if(err){console.log(err); }else{/ / If error is empty, success is achieved, there is no error console.log(data); }})Copy the code

The first argument to a callback is always an error object

The callback function seems to solve the problem, but in the process of using it, we will inevitably find that the callback function also has its shortcomings:

  1. The reason for not catching a try catch is that an attempt to try/catch an asynchronous method will catch only exceptions within the current event loop, not exceptions thrown during call back execution. That is, when you are about to catch an exception, an asynchronous error occurs when the try catch block ends.
  2. An asynchronous operation cannot return. For example:

// Send an Ajax request
$.ajax({
    type: "POST".url: "checkName.php".data: {
        username: $inputVal
    },
    success: function(responseText) {
        if (responseText === "helloworld") {
                 return false;
        } else {

                return true; }}});// Cause: asynchronous operation, may not be able to fetch data directly return, so wait for undefined.


Copy the code

3. Too many asynchronous operations can cause callback hell.


fs.readFile('./template.txt'.'utf8'.function (err, template) {
  fs.readFile('./data.txt'.'utf8'.function (err, data) {    
    console.log(template + ' '+ data); })})Copy the code


2. Solve the callback nesting problem

1. Publish subscriptions through events

Code first:

let EventEmitter = require('events');let eventByDep = new EventEmitter();
let html = {};

// Listen for a data acquisition success event and call the callback function when the event occurs

eventByDep.on('ready'.function(key,value){ 
     html[key] = value;  
     if(Object.keys(html).length == 3) {// Prints out the value of template
      console.log(html); }}); fs.readFile('./data1.txt'.'utf8'.function (err, data1) { 
 // the event name 2 argument is passed to the callback function
    eventByDep.emit('ready'.'data1',data1);
})

fs.readFile('./data2.txt'.'utf8'.function (err, data2) {  
    eventByDep.emit('ready'.'data2',data2);
})Copy the code

2. Handle this by setting a sentry function

Advantages: No additional packages need to be imported

The sentry function is called to monitor the asynchronous operation of the file. The code is as follows:

// The sentry function is used to listen for the return value of asynchronous file operations.
function done(key,value){  html[key] = value; 
 if(Object.keys(html).length == 2) {console.log(html);  
  }
}

fs.readFile('./data1.txt'.'utf8'.function (err, data1) { 
    done('data1',data1);
})
fs.readFile('./data2.txt'.'utf8'.function (err, data2) { 
 done('data2',data2);
})


// We can also write an advanced function to generate the sentry function:
function render(length,cb){  let html={};  return function(key,value){   
   html[key] = value; 
   if(Object.keys(html).length == length){ cb(html); }}}let done = render(3.function(html){ 
    console.log(html);
  });


fs.readFile('./data1.txt'.'utf8'.function (err, data1) {
     done('data1',data1);
})fs.readFile('./data2.txt'.'utf8'.function (err, data2) { 
     done('data2',data2);
})
fs.readFile('./data3.txt'.'utf8'.function (err, data3) {  
     done('data3',data3);
})Copy the code


3. The arrival of the promise

The above asynchronous processing, have more or less shortcomings, then the arrival of promise, so that JS asynchronous processing becomes convenient, elegant. So how does promise work?

3.1 Implement a simple Promise manually

1. First, we created three state constants to represent three states: suspended, failed and successful.

2. Initially, the state of the Promise should be pending

3. The value variable is used to save the value passed in resolve or reject

4. ResolvedCallbacks and rejectedCallbacks are used to hold callbacks in then. Since the state may still be waiting when a Promise is executed, callbacks in then should be saved for state changes.

5. The specification states that only PENDING states can be changed to other states.

6. The then method has two functions, which are failed and successfully executed callbacks.

// Create three states
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'

function Promise(executor){
  const self = this
  self.state = PENDING   // Default pending self.value = null
  self.resaon = reason   // Cause of failure
  self.resolvedCallbacks = []  self.rejectedCallbacks = []   
  / / success
  function resolve(value){
      if (self.state === PENDING){
         self.state = RESOLVED
         self.value = value 
        // Execute the callback function
         self.resolvedCallbacks.forEach(fn= >fn())    
      }
   }
  
  / / failure mode
  function reject(reason) { 
     if (self.status === 'pending') {    
         self.status = 'rejected';         
         self.reason = reason;        
         self.rejectedCallbacks.forEach(fn= >fn()) 
        }   
    }
   try {
      executor(resolve, reject)}catche(e){  
       // An exception occurred during the capture
      reject(e);
   }}


// Implement the promise.then method
Promise.prototype.then = function(onFulfilled, onRejected) { 
   const self = this
  // Judge ondepressing,onRejected whether this is a function type, if not, then assign the value to the corresponding parameter, and realize transparent transmission.
   onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v= > v
   onRejected =
    typeof onRejected === 'function'
      ? onRejected
      : r= > {
          throw r
        }
   if (self.state === PENDING) {   
     self.resolvedCallbacks.push(onFulfilled)  
     self.rejectedCallbacks.push(onRejected)  }
   if (self.state === RESOLVED) {    
     onFulfilled(self.value) 
  }
  if (that.state === REJECTED) {
     onRejected(self.value)  }
}


/ / use
new Promise((resolve, reject) = > {
  setTimeout((a)= > {
    resolve(1)},0)
}).then(value= > {
  console.log(value)
})Copy the code


This is just a simple version of promise.

So promise solves the callback hell of asynchronous callbacks.


4. Use Generator + Promise to implement asynchrony

First let’s get to know the Generator

  1. We use * to identify a Generator function.
  2. Each execution is stopped at yield. Call next once and the execution continues.
  3. The result is an iterator that has a next method
  4. Yield is followed by the value of value
  5. The yield equals is preceded by the value passed in by our current call to Next
  6. The first next pass was invalid.

Example code is as follows:

function* read() { 
   console.log(1);   
   let a = yield 'hello';    
   console.log(a);    
   let b = yield 9    
   console.log(b);    
   return b;
}
let it = read();
console.log(it.next(' ')); // {value:'hello',done:false}
console.log(it.next('100')); // {value:9,done:false}
console.log(it.next('200')); // {value:200,done:true
}console.log(it.next('200')); // {value:undefined,done:true}Copy the code

Asynchrony is generally implemented using Generator + Promise:

let bluebird = require('bluebird');let fs = require('fs'); // Make the file read Promiselet read = bluebird.promisify(fs.readFile);
  function* r() {    
    let content1 = yield read('./1.txt'.'utf8'); 
    let content2 = yield read(content1, 'utf8'); 
    returncontent2; } // Use next to perform asynchronylet g = r();
g.next().value.then(function (data) {
  g.next(data).value.then(function (data) {
    g.next(data)
  })
})
Copy the code

Next we use the CO library (which we implement ourselves) to iterate the generator automatically

function(it){
    return new Promise(function(resolve,reject){
            function next(data){
                let {value,done} = it.next(data);
                if(! done){ value.then(function(data){
                        // Pass the value down
                        next(data)
                  },reject)
                }else{ resolve(value) } } next(); })}/ / use
co(r()).then(function (data) {   
   console.log(data)
})
Copy the code


5.async + await

The syntax is as follows:

// Can only be used inside async functions
let value = await promiseCopy the code

The keyword await makes JavaScript wait until a promise executes and returns its result.

That is to say async + await is equivalent to CO + generator it implements a syntactic sugar.

Examples are as follows:

let bluebird = require('bluebird');
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);
async function r(){ 
  try{     
    let content1 = await read('./text'.'utf8');
    let content2 = await read(content1,'utf8');       
    return content2;   
 }catch(e){ 
// Catch if something goes wrong
  onsole.log('err',e)    
 }
}


// Async returns promise,
r().then(function(data){   
 console.log('flag',data);
},
   function(err){   
    console.log(err);
})Copy the code

So what are the benefits of async+await?

  1. Fixed callback hell
  2. Execute asynchronously and return results synchronously at the same time.

  3. Fixed the return value problem

  4. You can implement a try/catch of your code


The above is my summary of the development of javascript, shortcomings, but also ask you a lot of big guy correction. Thank you very much!