Writing in the front

Asynchronous programming of Javascript plays an important role in daily front-end business development. Common asynchronous programming includes callback function, event listener, Promise, Generator, Async/await.

So:

  • What’s the difference between synchronous and asynchronous programming?
  • What are some ways to fix callback hell?
  • How many states do promises have inside them?
  • How does Promise solve callback hell?
  • What does Generator return after execution?
  • How is Async/await better than Promise and Generatir?

Synchronous and asynchronous

Synchronization: When a piece of code is executed, the rest of the code is blocked until the result is returned, but once the execution is complete and the return value is returned, the other code can be executed.

Asynchronous: When a piece of code executes an asynchronous procedure call, the code does not immediately return the result, but suspends execution in the background. After an asynchronous call is made, the call is typically processed by a callback function before the result is retrieved.

We know that Javascript is single-threaded, and it can block if the JS code is executed synchronously. If used, it does not block, and you do not need to wait for the result of asynchronous code execution, and can continue to execute the code logic after the asynchronous task.

So how is the implementation of JS asynchronous programming developed?

Earlier years in order to achieve ASYNCHRONOUS PROGRAMMING JS, the general use of callback function way, such as: more typical event callback, but the use of callback function to achieve there is a very common problem, is callback hell. See if the code below looks like a Russian doll.

fs.readFile(a,"utf-8".(err,data) = >{
    fs.readFile(b,"utf-8".(err,data) = >{
        fs.readFile(c,"utf-8".(err,data) = >{
            fs.readFile(d,"utf-8".(err,data) = >{... })})})})Copy the code

Common asynchronous programming scenarios are:

  • Callback of ajax request
  • Callback in timer
  • Event callback
  • Some method callbacks in Node.js

The readability and code maintainability of asynchronous callbacks may be acceptable for a while if the hierarchy is small, but when the hierarchy becomes large, callback hell descends.

Promise

In order to solve the problem of callback hell, the community put forward the solution of Promise, ES6 has written it into the language standard, using the Promise implementation way to solve the problem of callback hell to some extent.

Promises are simply a container that holds the result of an event that will end in the future. Syntactically, a Promise is an object that can get asynchronous action messages. Promises have three states:

  • Pending: An initial state that has neither been completed nor rejected
  • This is a pity that the operation is completed successfully
  • Rejected: The operation fails

As for state switching of Promise, if you want to further study it, you can learn “finite state machine” knowledge points.

The implementation of a Promise object in a pending state is either completed with a value or rejected for a reason. When the pending state changes to a complete or rejected state, we can make a chain call using the form promise.then. Because the final promise.prototype. then and promise.prototype. catch methods return a Promise, they can continue to be called chained.How did Promise end with a callback to the hell question?

  • Solve the multi-layer nesting problem
  • There are two possibilities (success or failure) for each task. You need to handle the two possibilities respectively after each task is executed

Promise tackles callback hell with three main techniques: callback function delay binding, return value penetration, and error bubbling

Promise.all

Promise.all(iterable) can pass an iterable as a parameter, which is useful for summarizing the results of multiple promises. In ES6, multiple promise.all asynchronous requests can be processed in parallel. Success is returned in order when all results are returned successfully, and failed methods are entered when one of the methods fails.

Promise.all(iterable);
Copy the code

Use promise.all to solve the asynchronous programming problem above.

function read(url){
  return new Promise((resolve,reject) = >{
    fs.readFile(url,"utf-8".(err,data) = >{
      if(err) return err;
      resolve(data);
    })
  })
}

read(A).then(data= >{
    return read(B);
}).then(data= >{
    return read(C);
}).then(data= >{
    return read(D);
}).catch(reason= >{
    console.log(reason);
})
Copy the code

We saw above that the use of promises improved the solution to callback hell, but it was still not very well maintained, and there was a new approach to it.

function read(url){
  return new Promise((resolve,reject) = >{
    fs.readFile(url,"utf-8".(err,data) = >{
      if(err) returnerr; resolve(data); })})}// Promise.all allows multiple asynchronous parallel executions, all at the same time to get the final solution
Promise.all([read(A),read(B),read(C)]).(data= >{
    console.log(data)
}).catch(reason= >{
    console.log(reason);
})
Copy the code

Promise.allSettled

Promise.allsettled has a similar syntax to promise.all, which accepts an iterable as an argument and returns a new Promise. When Promise.allSettled is processed, we can get the state of each Promise, whether or not it was processed successfully.

Promise.allSettled(iterable);
Copy the code

Promise.any

Promise.any also takes an iterable as an argument, and the any method returns a Promise. As long as one of the parameter Promise instances becomes the fulfilled state, the final instance returned by any will become fullfiled state. If all the parameter Promise instances change to the Rejected state, the final instance returned by Any will change to the Rejected state.

Promise.race

Promise.race takes an iterable as a parameter, and the race method returns a Promise, which changes state as soon as one of the parameters changes state first.

Promise method role
all This parameter is returned only when all returns are success
allSettled Parameter Returns the execution status of each parameter regardless of whether the return result was successful
any Returns the execution result of any success in the argument
race Returns the result of the first successfully executed argument

Generator

Generator Generator is the new keyword in ES6. Generator is a function with an asterisk that can be used to pause or execute functions in conjunction with the yield keyword.

One of the most important features of a Generator is that it can yield execution of functions. Generator functions can be thought of as containers for asynchronous tasks, using yield syntax where they need to be paused.

function* gen(){
  let a = yield 111;
  console.log(a);
  let b = yield 222;
  console.log(b);
  let c = yield 333;
  console.log(c);
  let d = yield 444;
  console.log(d);
}

let t = gen();
t.next(1);// The first time the next function is called, the argument passed is invalid, so the result cannot be printed
t.next(2);/ / 2
t.next(3);/ / 3
t.next(4);/ / 4
t.next(5);/ / 5
Copy the code

In the above code, calling gen() blocks the program and does not execute any statements; On the other hand, a call to g.ext () will continue execution until the yield keyword is paused; The next method keeps executing, and finally returns an object with two properties: value and done.

Yield is also an ES6 keyword. In conjunction with Generator execution and pausing, yield returns an iterator object with both value and done, value indicating the returned value and done indicating whether or not the current iteration is complete.

function* gen(){
  yield 1;
  yield* gen2();
  yield 4;
}

function* gen2(){
  yield 2;
  yield 3;
}

const g = gen();
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
Copy the code

Running results:

So what does Generator have to do with asynchronous programming? Is it possible to execute the Generator functions sequentially at once?

Thunk function

The basic idea of the thunk function is to take a certain number of arguments, generate a custom function, and then use the custom function to do the desired function.

const isType = type= > {
  return obj= > {
    return Object.prototype.toString.call(obj) === `[object ${type}] `; }}const isString = isType("string");
const isArray = isType("Array");

isString("yichuan");//true
isArray(["red"."green"."blue"]);//true
Copy the code
const readFileThunk = filename= >{
  return callback= >{ fs.readFile(filename,callback); }}const gen = function* (){
  const data1 = yield readFileThunk("a.txt");
  console.log(data1.toString());
  const data2 = yield readFileThunk("b.txt");
  console.log(data2.toString());
}

const g = gen();
g.next().value((err,data1) = >{ 
  g.next(data1).value((err,data2) = >{ g.next(data2); })})Copy the code

We can see that the above code still looks like a Russian doll, so we can optimize it as follows:

function fun(get){
  const next = (err,data) = >{
    const res = gen.next(data);
    if(res.done) return;
    res.value(next);
  }
  next();
}

run(g);
Copy the code

The CO library is used to handle the automatic execution of Generator functions. The core principle of the co library is that it is packaged into a library with the thunk function and Promise object described earlier.

The Generator function is a container for asynchronous operations. The CO function takes the Generator as an argument and returns a Promise object. In the returned Promise object, CO first checks if the parameter gen is a Generator. If so, execute the function; if not, return directly and change the state of the Promise object to Resolved. Co wraps the next method of the internal pointer object of the Generator function as an ondepressing function, mainly in order to catch thrown errors. The key is next, which calls itself over and over again.

const co = require("co");
const g = gen();
co(g).then(res= >{
  console.log(res);
})
Copy the code

Async/await

Asynchronous JS programming has evolved from the original callback method to the use of Promise objects to the Generator+ CO method, with each change somewhat but not completely. Async /await is called the ultimate asynchronous solution in JS. It is possible to write asynchronous code synchronously, like Generator+ CO functions, with underlying syntax support, without recourse to any third party libraries.

Async is the syntactic sugar of Generator functions and async/await has the advantage of clean code and can handle callbacks.

function testWait(){
  return new Promise((resolve,reject) = >{
    setTimeout(() = >{
      console.log("testWait");
      resolve();
    },1000); })}async function testAwaitUse(){
  await testWait();
  console.log("hello");
  return "yichuan";
}
// The output sequence is: testWait Hello yichuan
console.log(testAwaitUse());
Copy the code

Asynchronous programming methods summary

JS asynchronous programming Simple summary
The callback function The most asynchronous programming method
Promise New syntax in ES6 to solve callback hell
Generator Used with yield, the iterator is returned
async/await Used together, async returns a Promise object and await controls the execution order

Refer to the article

  • The Core Principles of Javascript
  • Javascript Advanced Programming
  • Javascrtipt you Don’t Know
  • Six schemes of JS asynchronous Programming

Write in the last

This article mainly introduces one of the most important knowledge points of Javascript, which is also a concept that is often contacted in later development work. The commonly used asynchronous programming methods are: callback function, Promise, Generator and async/await. Promise is the solution to the callback hell caused by frequent use of callback functions, but Promise’s chained functions are also long, which is the ultimate solution to async/await.