When I first got in touch with JS, I was scared to death of ES6 promise, async and await. I was even afraid of the word promise for a while. As I gradually got in touch with it later, I felt that it was not so difficult to understand, and I mainly needed to understand some basic knowledge of JS. So follow my lead and understand promise, async and await once and for all.

There are three important things to know about this series:

1, about what synchronous, asynchronous, which involves some stack and message queue, event polling knowledge; 2. Several solutions for asynchronous programming, mainly callback functions and promises; 3. Generator functions, the ultimate solution for asynchronous programming, and their syntactic sugars async and await.Copy the code

If you want to understand promises, you need to understand what asynchrony is and what synchronization is. This article will focus on what asynchrony is and what asynchrony is.

Where does JS come from?

The emergence of any new language must be related to his needs at the time, JS stands for Javascript, was born in 1995 (the same age as me). It was originally created as a form submission cue. Before THE advent of JS, all forms had to be submitted to the server to verify required fields.

For example, if you want to apply for a QQ number, you fill in a lot of information, and then you realize that your mobile phone number is missing a bit and you re-enter it, then you must have broken the heart of the computer. At this time, JS was born, because it is real-time interaction with users, so it was first called livescript. At that time, in order to catch the popularity of Java, When the account was changed to Javascript, accidentally grow up can be equal to Java.

Why is JS single threaded

Js has been single threaded since its inception, so why single threaded? To make it easier for us veggie chickens to get started? Of course not.

Js’s main purpose is to operate the DOM, and the interaction with the user, which determine that he is only a single thread, such as the thread you created a DOM, the thread to delete, by this time the browser should which one shall prevail, so this should never be changed, you front end can be made in the development of the rocket, js, certainly also is single threaded.

What are synchronous and asynchronous

1. What is synchronization?

You can only do one thing at a time. Today, after work early, you want to call your girlfriend, she may be having dinner with other friends, because the mobile phone is silent, so you can’t hear, you keep calling, calling, doing nothing, wasting time, this is called synchronization. Since JS is single threaded, it is synchronous from childhood.

Here's a code:  function second() { console.log('second') } function first(){ console.log('first') second() console.log('Last') } First () this is very simple, execute the print result: first, second, lastCopy the code

So what happens when JS executes this code? There is also the concept of a ‘call stack’

2. Call stack

Don’t panic, it’s not that complicated, you can understand it as a toilet, we go to the toilet, but! It’s not fifO, it’s fifO. Using the concept of a call stack, explain the order of execution of the above code:

When this code executes, a global execution context is created and pushed to the top of the call stack; The first() function goes first, and now it's at the top; Then you print 'first', and when you're done, the console.log pops out. Now first is still at the top, and second is underneath it, so it won't bounce away; Execute second() with second at the top; Print 'second' and when you're done, pop the console.log with Second at the top; At this point, second is done, it's gone, first is at the top; The browser says, "First," do you have anything else? "First says," I have one more."Copy the code

What is asynchrony?

When the phone doesn’t go through, you send a text message to your girlfriend, take a shower, tell me when you get home, and I’ll call you back. That’s asynchronous. Later, in order to improve efficiency, multiple kernels of the browser are used. HTML5 proposes the Web Worker standard, which allows JavaScript scripts to create multiple threads, but the child threads are completely controlled by the main thread and cannot operate DOM.

So this doesn’t affect the single-threaded nature of JS, js can still do one thing at a time, it just takes a shower earlier.

Const getList = () => {setTimeout(() => {console.log(' I did it! '); }, 2000); }; console.log('Hello World'); getList(); Console. log(' ha ha ha '); The execution order is: Hello World, ha ha ha, I executed! (Execute last one after 2 seconds)Copy the code

So what happens when this code executes? This place has a ‘message queue’ concept again, not panic!

4. Message queues

As we said earlier, during synchronization, the browser maintains an ‘execution stack’. In addition to the execution stack, when multithreading is enabled, the browser also maintains a list of messages. Except for the main thread, all the other sub-threads are called the message list.

Let’s examine the above code using the concept of a message list:

Console. log('Hello World') is executed first, the browser looks at it, central army (main thread)! You first. Then the getList function executes, the browser sees setTimeout, and you are eight L (sub-thread)! Just pull over and wait; Then console.log(' ha ha ha ') is executed, central army (main thread)! You also. Then the browser asks, is there still central Army? No, eight L's on the way!Copy the code
Add difficulty:
SetTimeout (function() {console.log(' I am the timer! '); }) new Promise(function(resolve) {console.log(' I am Promise! '); resolve(); }). Then (function() {console.log(' I am then! '); }) console.log(' I'm the main thread! '); Order of execution: I am Promise! I'm the main thread! I was then! I'm a timer!Copy the code

Why does promise. Then execute before timer? This involves the concept of ‘event polling’.

5, the first event polling

In order to improve the efficiency of the browser, we have enabled a different multi-thread for JS, because JS cannot be executed at the same time. This process can be understood as event polling. Let’s first examine the above code in order of event polling, and then introduce the concept:

The promise function must be executed first, it's the main thread, so print 'I'm promise'; Then go to the main thread and print 'I am the main thread'; Then the main thread runs out, and the message list begins to run; (macro and micro tasks will be covered in a moment.) At this point, a promise is executed. Then, because it is a micro task, the 'I am then! 'Message list in the top is timer, but timer is a macro task, the priority is low, so it will be back;Copy the code

6. What is a macro task? Micro tasks?

SetTimeout, setInterval, XMLHttprequest, setImmediate, I/O, UI Rendering, etc. ** Microtasks: ** Promise, process.nextTick (node environment), Object.observe, MutationObserver, etc. Microtasks are better than macro tasks. (1) Execute the main code block, which is also a macro task. (2) If a Promise is encountered, put the content after then into the microtask queue. (3) Encounter setTimeout, put it into the macro task. Check whether there are tasks in the microtask queue. (5) Execute all microtasks if there are tasks. (6) Start the next macro task after the execution.Copy the code

7. Then the process 2, 3, 4, 5, and 6 are called event polling.

Here thank the gold digger god’s article, to show respect, hang the address!

Juejin. Cn/post / 684490…

The callback function

As mentioned above, both macro and micro tasks are asynchronous, including Ajax requests, timers, etc. Let’s take a look at promise, which is a way to solve asynchrony. What are the common methods? The first is the callback function.

Function f2() {console.log('2222')} function f1(callback){console.log('111')     setTimeout(function () {         callback();     }, 5000);     console.log('3333') } f1(f2); First, the print value is: 111, 3333, 2222 in five secondsCopy the code

When the main thread is finished, the f2 function will be called via the callback function. But look at the following example:

Now we read a file, Var reader = new fileReader () var file = input.files[0] reader.readAsText(file, 'utf-8',function(err, data){ if(err){ console.log(err) } else { console.log(data) } })Copy the code

Now that looks pretty good, but if something goes wrong with the file upload, we have to make a judgment call in the callback, what if we read this file and then we have to read multiple files? Should I write:

Var reader = new FileReader() var file = input.files[0] readAsText(file1,) var file = input. 'utf-8',function(err1, data1){ if(err1){ console.log(err1) } else { console.log(data1) } reader.readAsText(file2, 'utf-8',function(err2, data2){ if(err2){ console.log(err2) } else { console.log(data2) } reader.readAsText(file3, 'utf-8',function(err3, data3){ if(err3){ console.log(err3) } else { console.log(data3) } }) }) })Copy the code

This will do, but the code will be less readable and less elegant, which is often referred to as’ callback hell ‘. So how do you crack this nested callback? ES6 provides us with promises:

Third, the promise

First of all, let’s take this literally. What is promise? I promise you I’ll make a promise.

My girlfriend asked me to do something. I haven’t finished it yet, but I promise you that it will be fulfiled or rejected and pending.

Let promise = new promise ((resolve, Reject) => {setTimeout(() => {resolve(2000) reject(2000)} 1000) console.log(1111)}) promise.then(res => {console.log(res) // then Err =>{console.log(err)// then the second parameter will capture the failure result}) print the result: 1111 2000 (one second later)Copy the code

1. Then chain operation

The then method of the Promise object returns a new Promise object, so you can call the THEN method chained.

The then method takes two functions as arguments. The first argument is the callback if the Promise execution succeeds, and the second argument is the callback if the Promise execution fails. This example makes it clear that the second argument captures the failed callback.

Only one of the two functions is called. When your girlfriend asks you to make tomato and egg soup, you either do it or you don’t, order takeout, and there’s definitely no third choice.

The return value of the function will be used as a Promise object to create a return for THEN. How to understand this sentence? Here’s another example:

let promise = new Promise((resolve, reject) => { setTimeout(() => { resolve(2000) }, 1000) console.log(1111)}) promise.then(res => {console.log(res)) }). Then (res => {console.log(res) // prints the same value as the previous Promise return}) so the print order should be: 1111 2000 3000Copy the code

We’ve just seen that then takes two arguments, a successful callback and a failed callback, which doesn’t seem very elegant. In addition to then, a promise also provides a catch method:

2. Catch operation

This catch is specifically for catching bad callbacks, but let’s start with an example:

Let promise = new promise ((resolve, reject) => {setTimeout(() => {reject(2000)) 1000) console.log(1111)}) promise.catch(res => {console.log(res) // catch)Copy the code

In addition to then and catch, promise has two syntax, all and race, which we’ll use briefly:

3, all

Now we have A requirement, A total of three interfaces A, B, C, must be successful, can launch the fourth request, how to implement?

Chain calls
let getInfoA = new Promise((resolve, Reject) => {console.log(' reject ') resolve()}). Then (res => {let getInfoB = new Promise((resolve, Reject) => {console.log(' reject ') resolve()}). Then (res => {let getInfoC = new Promise((resolve, Resolve ()}). Then (res => {console.log(' all done! ')})})})Copy the code

Layer upon layer. It’s not that elegant

all
let getInfoA = new Promise((resolve, Reject) => {console.log(' reject ') resolve()}) let getInfoB = new Promise() Reject) => {console.log(' reject ') resolve()}) let getInfoC = new Promise((resolve, Reject) => {console.log(' little C started ') resolve()}) promise. all([getInfoA, getInfoB, GetInfoC]). Then (res => {console.log(' All done! ')})Copy the code

It receives an array of Promise objects as arguments, and only calls the THEN method when all of the Promise objects in this array are resolved or Rejected. It’s perfect, it’s elegant.

4, race

Now I have another requirement, also interface A, B, C, as long as there is A response, I can call interface D, so how to implement?

The traditional way
let getInfoA = new Promise((resolve, Reject) =>{console.log(' reject ') setTimeout((err =>{resolve(' reject ')}),1000)}). Then (res =>{console.log(res)}) let getInfoB = new Promise((resolve, Reject) =>{console.log(' reject ') setTimeout((err =>{resolve(' reject ')}),1001)}). Then (res =>{console.log(res)}) let getInfoC = new Promise((resolve, Reject) =>{console.log(' reject ') setTimeout((err =>{resolve(' reject ')}),1002)}). Then (res =>{console.log(res)}) Result Little A starts to execute little B starts to execute little C starts to execute little A the fastestCopy the code

This method has to be written three times, which is not so elegant. Let’s see how race should be written.

race
let getInfoA = new Promise((resolve, Reject) => {console.log(' reject ') setTimeout((err => {resolve(' reject ')}),1000)}) let getInfoB = new Promise((resolve, Reject) => {console.log(' reject ') setTimeout((err => {resolve(' reject ')}),1001)}) let getInfoC = new Promise((resolve, Reject) => {console.log(' reject ') setTimeout((err => {resolve(' reject ')}),1002)}) promise. race([getInfoA, getInfoB, GetInfoC]). Then (res => {console.log(res)}) Results Little A starts to execute little B starts to execute little C starts to execute little A the fastestCopy the code

Like promise. all, promise. race takes an array of Promise objects as arguments, except that the. Then method can be called when one of the Promsie states in the array changes to Resolved or Rejected.

Promise is a method that ES6 uses to solve asynchrony. Now it is widely used, such as axios, which is wrapped with promise. It is very convenient to use.

I talked a little bit about callbacks and promises for asynchronous programming, and using promises to solve asynchronous programming, if you make multiple calls, it doesn’t look so comfortable.

In addition to promise, ES6 also provides us with more powerful async and await. Async and await are syntax-candy of Generator functions. If we want to fully master the use of async and await, we must master the use of Generator functions.

Iv. Generator functions

What is a Generator function?

Generator functions are the implementation of coroutines in ES6. The most important feature of Generator functions is that they can surrender execution of functions (i.e., suspend execution).

You can think of it this way: this function can’t be executed by itself, so someone else has to do it for you, kick (next()), and take a step.

Basic usage:

Function * doSomething() {yield 'eat' return 'sleep'} let newDoSomething = doSomething(); Log (newdosomething.next ()) // {value: "eat ", done: False} console.log(newdosomething.next ()) // {value: "sleep ", done: true}Copy the code
As you can see from the above example, a Generator function has four characteristics:

Function = function = function = function = function

function* doSomething(){}
function *doSomething(){}
Copy the code

2. There is a yield in the function, which interrupts the function into different states.

A yield can be cut into two states, requiring two next() triggers;Copy the code

The Generator does not execute on its own, but returns a traverser object.

4. The iterator object calls each state in turn through the.next() method.

The messaging

In addition to controlling the execution of stateless functions, Generator functions also have an important function for message passing.

function *doSomething() { let x = yield 'hhh' console.log(x) return (x * 2) } let newDoSomething = doSomething() Console. log(newdosomething.next (1)) console.log(newdosomething.next (2)) {value: "HHH ", done: false} 2 {value: 4, done: true}Copy the code

Analyze why this is printed: (Important)

//{value: "hhh", done: When x //2 is paused, the yield expression can receive the next(...) that starts it. When x //2 is paused, the yield expression receives the next(...) that starts it. //{value: 4, done: true} the value of x is assigned to 2, so print 2*2Copy the code
Note a few issues:
The first next() is used to start the Generator and is ignored
Yield is similar to return. You can use it as a return. If there is no value after yield, return undefined
Finally, the next() function returns the value of the function return, or undefined if it does not
Having thoroughly understood the above concept, let’s look at the chestnuts below:
function *doSomething() {
    let x = yield 'hhh'
    let y = yield (x + 3)
    let z = yield (y * 3)
    return (x * 2)
}

let newDoSomething = doSomething()

console.log(newDoSomething.next(1))  // {value: "hhh", done: false}
console.log(newDoSomething.next(2))  // {value: 5, done: false}
console.log(newDoSomething.next(100)) // {value: 300, done: false}
console.log(newDoSomething.next(1000)) // {value: 4, done: true}
Copy the code

Or use the above ideas to analyze:

Next (2), at which point x has been assigned to 2, so print 2+3. Next (100), at which point y has been assigned to 100, so print 100*3. Fourth next(1000), At this point y is already assigned 1000, but it prints x*2, so it prints 4Copy the code
Let’s look at a special case :(special is easy to pit)
function *doSomething() {
    let x = yield 'hhh'
    console.log(x)
    let y = yield (x + 3)
    console.log(y)
    let z = yield (y * 3)
    return (x * 2)
}

let newDoSomething = doSomething()

console.log(newDoSomething.next(1))
console.log(newDoSomething.next(2))
console.log(newDoSomething.next())
console.log(newDoSomething.next())
Copy the code

Take a look at the print result:

{value: "hhh", done: false}
2
{value: 5, done: false}
undefined
{value: NaN, done: false}
{value: 4, done: true}
Copy the code

Why print undefined?

1, next(1) {value: "HHH ", done: false} Second next(2) prints 2, second next(2) prints 2+3; 3, the third next() pass is empty, so y print undefined, undefined*3 must be NaN; 4, next() returns 2*2Copy the code

Async, await

1, what is async, await?

Async and await are the syntactic sugar of Generator functions. The principle is realized by Generator functions plus automatic executor. This makes async and await functions like ordinary functions and no longer need to be executed at next.

It takes advantage of Generator functions and executes them state-by-state with await, but not all the way to next.

Here’s another example:

Chestnut 1
function f() { return new Promise(resolve =>{ resolve('hhh') }) } async function doSomething1(){ let x = await f() } DoSomething1 () Printed result: HHHCopy the code

Looking at the above example, you can see that Async has three characteristics:

1. The function is preceded by an async modifier to prove that it is an asynchronous function. 2, await is an operator used to form an expression that blocks the following code. 3, await is a resolve value if a Promise object is awaited.Copy the code
Chestnut 2
async function doSomething1(){ let x = await 'hhh' return x } console.log(doSomething1()) doSomething1().then(res => { Console. log(res)}) prints the result: Promise {<pending>} HHHCopy the code

Analyzing the chestnuts above gives you these two characteristics:

1. Async returns a Promise object. The value returned by the return inside the function becomes an argument to the then method callback. 2, await an expression if it is not a promise.Copy the code

2. Use in async and await projects

Now there is an encapsulated method to get data. Let’s use promise, Generator and Async to make requests respectively.

function getList() {
    return new Promise((resolve, reject) =>{
        $axios('/pt/getList').then(res => {
            resolve(res)
        }, err => {
            reject(err)
        })
    })
}
Copy the code

promise

Function initTable() {getList().then(res => {console.log(res)}).catch(err => {this.$message(err) // Element syntax})} Then you can call it directly and it looks pretty neat, but if you have multiple requests it's.then,.then looks pretty uncomfortableCopy the code

The Generator function

function *initTable(args) { const getList = yield getlist(args) return getList } function getList() { const g = initTable(this.searchParams) const gg = g.next().value gg.then(res =>{ this.total = res.data.count if (res.data.list) { this.tableList = res.data.list this.tableList.forEach(e => { e.receiveAmt = format(e.receiveAmt) }) } else { This.tablelist = []}})} this.tablelist = []}})Copy the code

async await

Async initTable() {// table list const getData = await getList(this.searchParams) return getData}, GetList () {this.inittable ().then(res =>{this.tablelist = res.data.list})} getList() {this.inittable ().then(res =>{this.tablelist = res.data.list})Copy the code

The above is my personal opinion on promise, async and await. If there is anything wrong, please leave a message or add me to wechat for communication.

Personal wechat official account: Little Jerry said that he usually sends some technical articles and reading notes, welcome to exchange.

The back will continue to update some js based articles, good-looking brothers and sisters point a concern.