ES6 points north [7] – from callback hell to promise and async / await

Time:2021-5-6

1 synchronous and asynchronous

1.1 JS can only do one thing at a time

First, we need to understand that JS is a single threaded language,You can only do one thing at a time

We can go through itJS executionandDOM renderingShare one threadTo understand this principle:

  • When JS modifies the DOM, the browser will not render the DOM, that is, the DOM rendering is blocked

And the above kind of blocked behavior, we call itIt is synchronous

1.2 concepts of synchronization and asynchrony

synchronization

  • english:Synchronization, usually abbreviated asSync
  • Definition: the synchronization behavior corresponds to the sequential execution of processor instructions in memorySequential execution of JS code(don’t think it’s nonsense. Compare it with the asynchronous definition below.)
  • In fact, the code has to wait for the result before it can continue

For example, in the following example, the recursive function blocks the execution of the last sentence, so when you open the console, you will find that 2 is not output immediately

function wait() {
    let start = new Date();
    While (New date() - start < 4000) {// block for 4 seconds
        
    }
      console.log(2);
}

//Core logic
console.log(1)
wait();
console.log(3); //  The execution of this sentence was blocked for four seconds

//Input result: 1 2 3

asynchronous

  • english:Asynchronization, usually abbreviated asAsync
  • Definition: similar toInterrupt in operating system, i.eEntities outside the current processsuretriggerCode execution
  • In fact, the code can continue without waiting for the result

What do you mean, for example

function wait() {
    setTimeout(() => console.log(2), 4000)
}
//Core logic
console.log(1);
wait(); //  After 4 seconds, the result is obtained
console.log(3); //  If there is no asynchrony, this sentence will have to wait 4 seconds to execute, so thank you for asynchrony

//Input result: 1 3 2

2 asynchronous application

2.1 common asynchronous scenarios

1. Network request

// Ajax
console.log(1);
$.get(url1, data1 => {
    console.log(data1);
      console.log(2);
})
console.log(3);
// 1 3 2

2. Timed tasks, such as setTimeout and settimeinterval

console.log(1);
setTimeout(() => console.log(2), 1000);
console.log(3);
console.log(1);
setInterval(() => console.log(2), 1000);
console.log(3);

3. Event monitoring

//Load picture
console.log(1);

let img = document.createElement('img');
//Onload is a callback, which is triggered when the image is loaded
img.onload = () => console.log(2);
//After SRC assignment, the image will start loading
img.src = '/xxx.png';

console.log(3);
// 1 3 2

2.2 common asynchronous problems

2.1 image loading problem

//Prerequisite: the first time the user's browser requests this image, that is, the user's browser is not cached
Document. Getelementsbytagnames ('img ') [0]. Width // the width is 0

Why is width 0?
becauseWhen JS is running, IMG is not downloaded completely

Solution

let imgNode = document.getElementsByTagName('img')[0]
imgNode.addEventListener('onload',function () {
  //Callback
  console.log(this.width)
})

2.2 asynchronous questions of interview questions

//Suppose there are five li
let liList = document.querySelectorAll('li')
for (var i = 0; i < liList.length; i++) {
    liList[i].onclick = function () {
        console.log(i) // 5 5 5 5 5
    }
}

Why?
becauseThe onclick event is handled asynchronouslyWhen the user triggers the onclick event,The cycle is long over

And because VaR and I are promoted to global variables,I'm 5

Therefore, the above situation occurs

Solution 1 [execute function immediately to create independent scope (not recommended)]

//Suppose there are five li
let liList = document.querySelectorAll('li')
for (var i = 0; i < liList.length; i++) {
  !(function (j) {
    liList[j].onclick = function () {
      console.log(j) // 1 2 3 4 5
    }
  })(i)
}

Solution 2 [using let]

Let makes I a local variable in {} of the for loop

//Suppose there are five li
let liList = document.querySelectorAll('li')
for (let i = 0; i < liList.length; i++) {
  liList[i].onclick = function () {
    console.log(i) // 1 2 3 4 5
  }
}

3 the way to get asynchronous results — callback

3.1 what is callback?

Callback: pass the function as a parameter of other functions

function printInfo(info) {
    console.log(info);
}

function getInfo(fn) {
    FN ('dawang ');
}

//Printinfo is passed as a parameter of getinfo
getInfo(printInfo); // ' 'dawang '
  1. Printinfo isCallback function
  2. printInfoThe purpose of this function is to be able toCalled through getinfo
  3. Calling printInfo behavior in getInfo function isTrigger callback function

3.2 common callback forms

Error first form of node.js

First, judge whether the error exists. If it exists, it indicates that there is an error. If it does not exist, it is successful

fs.readFile('./1.txt', (error, content) => {
  if (error) {
    //Failure
  } else {
    //Success
  }
})

The success / error form of jquery

$.ajax({
    url: '/xxx',
    success: () => {
    },
    error: () => {
    }
})

The done / fail / always form of jquery

$.ajax({
    url: '/xxx',
}).done(() => {
}).fail(() => {
}).always(() => {
})

Another is the then form of prosmise, which will be explained in detail in Section 4

3.3 nested asynchronous callbacks — callback hell

In JS, we can solve the asynchronous problem through callback

But the bigger difficulty is thatHow to solve the problem of series asynchronousBefore promise, the general solution wasNested asynchronous callbacks, also known asBack to hell

Back to hell: callback set callback set callback set callback set callback set callback set callback set callback set callback set

The following example vividly shows that asynchronous results are handled by callback

Just nested callbacks all the time

//Get data1
$.get(url1, data1 => {
    console.log(data1);
        
    //Get data1 and then data2
    $.get(url2, data2 => {
        console.log(data2);
        
        //Get data2 and then data3
        $.get(url3, data3 => {
            console.log(data3);
                        
            //... can be repeated indefinitely
        })
    })
})

4 Promise

4.1 take an example first

You need to know

  1. Axios is a library
  2. Axios() returns a promise instance
  3. You can think of Axios () as $. Ajax (), which are similar in function, except thatAxios follows promise specification

Please move the document of Axios

axios({
    url: './xxx.json'
}).then((resolve) => {
    console.log(resolve)
    Return 'I'm the second then'
}, (reject) => {
    console.log(reject)
}).then((resolve_2) => {
    console.log(resolve_ 2) //'I'm the second then '
}, (reject_2) => { 
    console.log(reject_2)
})

To prevent you from thinking about thiscall chaining I’m dazzled. I’ll simplify this

axios({
  url: './xxx.json'
}). Then (successful callback 1, failed callback 1)
  . then (success callback 2, failure callback 2)

I believe you must still be confused about the above code, because you can’t promise yet

Let’s first understand some basic concepts of promise

4.2 basic concept of promise

4.2.1 role of promise

Promise is designed to solve asynchronous programming problems,It avoids layer by layer nested callback functions [that is, callback hell]
Here is an asynchronous code written with the traditional method of callback hell
It’s very obvious how callback hell worksIt makes the readability of the code very bad

let src = 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png'

function loadImg(src, callback, fail) {
    let img = new Image()
    //Successful callback
    img.onload = function () {
        callback(img)
    }
    //Failed callback
    img.onerror = fail
    img.src = src
}

//The following code is actually loadimg (SRC1, success callback 1, failure callback 1);
//However, when successful callback 1 is executed, loadimg (src2, successful callback 2, failed callback 2) is executed again;
loadImg(src1, function (img) {
    console.log(img.width)
    loadImg(src2, function (img) {
        console.log(img.width)
    }, function () {
        console.log('error2')
    })
}, function () {
    console.log('error1')
})

4.2.2 new Promise()

new Promise(myExecutorFunc)Returns an instance of promise,myExecutorFuncThe requirement is a function, and its formal requirement is:

let myExecutorFunc = (resolve, reject) => {};

See for detailsMDN document

Here we mainly understandnew Promise()How to get the result of asynchronous operation

Generally, an asynchronous operation, such asaxios({url:'./xxx.json'})We definitely want to get a JSON object and pass it out

1. Many people often make the mistake of thinking that return can get the result, but this is just a big mistake

let p = new Promise(() => {
    let data = 3; //  This is the result we want to get
    return data; //  This return is useless
});

Enter P in the console:
ES6 points north [7] - from callback hell to promise and async / await

2.new Promise()It has to be done throughmyExecutorFuncTwo callback functions ofresolveandrejectPass on the ginseng

let p = new Promise((resolve, reject) => {
    let data = 3; //  This is the result we want to get

    resolve(data);
    //Or
    reject(data);
});

Enter P in the console:

ES6 points north [7] - from callback hell to promise and async / await

Of course, the above is just to show you the situation of promise instance P,To really deal with this result, you need toThen / catch and so onWe’ll talk about that later

let p = new Promise((resolve, reject) => {
    let data = 3; //  This is the result we want to get

    resolve(data);
    //Or
    reject(data);
});

p.then(data => {
    console.log(data); // 3
}, undefined);

4.2.3 three states of promise instance

  1. pendingIt’s the initial state
  2. Fully (resolved)Asynchronous operation is successful, which is transformed from pending
  3. rejectedThe asynchronous operation failed. It was transformed from pending

The promise instance represents the result of an asynchronous operation, andOnly the result of asynchronous operation can determine the current state

No other operation can change this state,Ding can be transformed into filled and rejected, but filled and rejected cannot be transformed into each other

We can see these three states through the console

  • Promise.resolve()Return aFully charged statePromise instance of
  • Promise.reject()Return aRejected stateAs shown in the figure below, the console reports an error, because rejected itself is aAsynchronous error type

ES6 points north [7] - from callback hell to promise and async / await

What’s the use of knowing these three states?
OK, let’s look at the code below

axios({
  url: './xxx.json'
}). Then (successful callback, failed callback)

yesaxios({url: '.'})For example, theReturns a promise object, i.eThe result of an asynchronous operation
Asynchronous operation succeededOn behalf ofpending -> fulfilled->In thenThe first parameter[successful callback]
Asynchronous operation failedOn behalf ofpending -> rejected->In thenThe second parameter[failure callback]

4.3 chain call of then and catch

becausePromise.prototype.thenandPromise.prototype.catchmethodReturns the promise object
So they can be usedcall chaining

OK, let’s take a closer look at the callback trigger mechanism

axios({
  url: './xxx.json'
}). Then (successful callback 1, failed callback 1)
  . then (success callback 2, failure callback 2)

ES6 points north [7] - from callback hell to promise and async / await

Is it a little dizzy? It doesn’t matter. Let me explain it in detail according to the example of Axios
ES6 points north [7] - from callback hell to promise and async / await

You must have some questions:

1.Why is the first then calledFailed callback 1The second then may also call theSuccessful callback 2What about it
A: because which callback function the second then call enters depends entirely on the status of promise returned by the first then call. In other words, lookWhether the asynchronous operation is successful or not

ES6 points north [7] - from callback hell to promise and async / await

Even if the first then callsSuccessful callback 1The second then is still possibleFailure callback 2Take a chestnut:

axios({
    url: './xxx.json'
})
.then((resolve_1) => {
    return Promise.reject();
    //In this case, the status of the project returned by then is rejected
}, (reject_1) => {})
.then((resolve_2) => {
    console.log(1)
}, (reject_2) => {
    //So the second then only calls its failure callback 2, which is executed here
    console.log(2)
})

2.Why don’t you mention catch?
becauseCatch is a grammatical sugar of then
Catch is equivalent to then only in the form of the second parameter failure callback
The above example uses catch, which can be written like this

axios({
    url: './xxx.json'
})
.then((resolve_1) => {
    return Promise.reject();
    //In this case, the status of the project returned by then is rejected
}, (reject_1) => {})
.catch((reject_2) => {
    //Callback to execute catch
    console.log(2)
})

//It is equivalent to
axios({
    url: './xxx.json'
})
.then((resolve_1) => {
    return Promise.reject();
    //In this case, the status of the project returned by then is rejected
}, (reject_1) => {})
.then(undefined, (reject_2) => {
    //So the second then only calls its failure callback 2, which is executed here
    console.log(2)
})

4.4 use promise by yourself

Above, we borrow itaxiosI learned the basic use of promise, but I only learned it on the basis of Axios encapsulating promise

Now I’ll learn how to package a promise by myself

4.4.1 promise without asynchronous logic

The first step

//Declare a function that returns a promise instance
let setPromise = function () {
    return new Promise();
}

Step two

let isSuccess = true; //  Control whether to execute resolve or reject

//New promise() takes a function
//It is specified that this function must have two parameters, which must be functions, that is, successful callback and failed callback
let setPromise = function () {
    //Resolve is a successful callback and reject is a failed callback
    let fn = (resolve, reject) => {
        If (issuccess) resolve ('success')// If resolve is executed, the promise state will be changed from pending to full
        Else reject ('failed ')// If you execute reject, the project status will be changed from pending > reject
    }
    return new Promise(fn)
}

You can guess, at this time the followingpromiseInstanceThe state of what, if putisSuccessChange tofalseWhat is the state? Give it a try

let promiseInstance = setPromise();
console.log(promiseInstance); // ?

Step 3: study the parameters

Read my notes carefully

let isSuccess = true; //  Control whether to execute resolve or reject

let setPromise = function () {
    let fn = (resolve, reject) => {
        //The two strings' success' and 'failure' passed here will become the parameters in the first then of the promise instance
        If (issuccess) resolve ('success');
        Else reject ('failed ');
    }
    return new Promise(fn)
}

let promiseInstance = setPromise();

promiseInstance
.then(param => {
    Console.log (param) //'success'
    Return 'success 2' // the return value will be the param in the next then
}, param => {
    console.log(param);
    Return 'failed 2'
})
.then(param => {
    Console.log (param) //'success 2 '
}, param => {
    console.log(param)
})

Again, if you putisSuccessChange tofalseWhat is the output? Give it a try

4.4.2 promise with asynchronous logic

Here is an example of rewriting Section 4.2.1 with promise

let src = ' https://avatar-static.segmentfault.com/198/713/1987139774-59c8bdbc3b36b_ Huge256 '// correct path
// let src = 'xxx'; //  Wrong path

let loadImg = new Promise((resolve, reject) => {
    let img = new Image();

    img.onload = () => {
        resolve(img);
    }

    img.onerror = () => {
        Reject ('picture loading failed ');
    }

    img.src = src;
})

loadImg.then(param => {
    Console.log ('image loaded successfully ');
    let body = document.querySelector('body');
    body.appendChild(param);
}, param => {
    console.log(param);
})

4.5 handling multiple promise

4.5.1 Promise.all()

Grammar: lookMDN document

effect

Use thePromise.resolve()Packaging, and then the results of packaging synthesisHowever, the synthesis results follow the following principles:

  1. If at least one promise turns out to bependingThe result of the synthesis is pending
  2. If at least one promise turns out to berejectThe result of synthesis is reject【This rule takes precedence over the previous one
let p1 = new Promise(() => {});

// pending + reject === reject
Promise.all([p1, Promise.reject('error')])
.then(param => {
    console.log(param)
}, param => {
    console.log(param) // error
})

Pass the value of promise.all solution:

  1. IfThe result is rejectedIt’s justThe first one is in the rejected statePass out the result of the promise instance of
  2. IfThe result is fulfilledThe result of all promise instances in the full stateEncapsulate into an arrayPass it on
//Rule one
Promise.all([Promise.reject('error1'), Promise.reject('error2')])
.then(param => {
    console.log(param)
}, param => {
    console.log(param) // error1
})
//Rule 2
Promise.all([Promise.resolve('success1'), Promise.resolve('success2')])
.then(param => {
    console.log(param) // [ 'success1', 'success2' ]
}, param => {
    console.log(param)
})

4.5.2 Promise.race()

Syntax: same as promise. All()

effect:

Use thePromise.resolve()Pack and return to the firstfulfilledorrejectedThe results of packaging

let p1 = new Promise((resolve) => {
    setTimeout(resolve, 1000, 'success');
})

let p2 = new Promise((resolve, reject) => {
    setTimeout(reject, 500, 'error');
})

//P2 ends first, so the result of project. Race() is rejected, passing the rejection reason to then
Promise.race([p1, p2])
.then(param => {
    console.log(param);
}, param => {
    console.log(param); // error
})

4.6 understanding promise in life language

The Chinese translation of promise isPromise to translate it for a period of aboutThe Chinese translation isthen
So,You can imagine that you go to buy oranges, but the store doesn’t have any in stock, and the clerk treats youPromiseAs long as it is fully delivered or no longer re jected,thenHe’ll let you know

5 async / await statement

5.1 async

5.1.1 async keyword declares an asynchronous function

Have a look firstasyncUse of: precede function declaration withasyncIt makes this function an asynchronous function

//The following FN are all declared as asynchronous functions by async
async function fn() {};
let fn = async () => {};

5.1.2 characteristics of asynchronous functions

1. Asynchronous functionAlways returns aPromise instance, itsessenceyesUse the return value of asynchronous function asPromise.resolve()packing

let fn = async () => {
    //If you don't write return, it's equivalent to return undefined
}

console.log(fn()); // Promise {<fulfilled>: undefined}

The effect of the above writing is almost the same as that of the following writing

let fn = () => {
    return Promise.resolve(undefined);
}

console.log(fn()); // Promise {<fulfilled>: undefined}

2. Asynchronous functionIn essence, it provides a running environment for awaitIf the await keyword is not included, its execution is the same as that of ordinary functions

What do you mean?

The async keyword just tells the browser that this function is an asynchronous function with asynchronous code,It is convenient for the browser to analyze it

But if you don’t write asynchronous code (just like the example above), the browser can’t help you. It’s only after parsing that you find that this is actually just a common function. Some preparations made by the browser for asynchronous functions are useless.

From this point of view,Async keywordIt’s just oneidentifier

5.2 await

5.2.1 usage of await

effect:awaitOperator is used to wait for aPromise instanceResultsIn essence, let promise. Resolve() wrap the expression after the await operator

Restrictions:Only in asynchronous functionsasync functionUsed in

Let’s start with an example

let result;
async function fn1() {
    let p = new Promise((resolve, reject) => {
        setTimeout(() => {
            Console.log ('after 10 seconds');
            resolve(1);
        }, 10000);
    })
    result = await p;
      console.log(2);
}

fn1();

In the process of running the code, we will find some problems:

  1. If you continuously enter result in the console, the console will constantly prompt:undefinedAfter 10 seconds, the result will be obtained
  2. console.log(2);This sentence was also executed after 10 seconds

ES6 points north [7] - from callback hell to promise and async / await

Why is that?
Because await will not execute until the promise instance P has a resultresult =This sentence and the following sentence
in other wordsawaitblockAfter that, the code is executed

5.2.2 await makes asynchronous code look like synchronous code

This title is a little bit convoluted. Let’s start with itSubsection 5.2.1Make a change to the example of, remove await, and see the result

let result;

async function fn1() {
    let p = new Promise((resolve, reject) => {
        setTimeout(() => {
            Console.log ('after 10 seconds');
            resolve(1);
        }, 10000);
    })
    result = p; //  Delete await
    console.log('result:', result); //  Print the result
    console.log(2);
}

fn1();

ES6 points north [7] - from callback hell to promise and async / await

We can clearly see that,console.log('result:', result);andconsole.log(2);It was carried out immediately

But! what the fuck?How can the result of result be an instance of promise in pending state?

It’s actually very simple, becauseResult is an instance of promise, an asynchronous code, andconsole.log('result:', result); It's a synchronization codeTherefore, you can only get the status of the result before it produces the final result, that is, pending.

Recombineawait, we passedresult = await p;You can get the final result of P directly,In fact, after the final result of P is generated, that is, after the end of asynchronous code, continue to execute the following code

And isn’t that how synchronization works?

In other words, await changes the execution order of the whole code by blocking the execution of synchronous code,It allows you to write asynchronous code in a synchronous way

5.3 details of await

5.3.1 await a fully promise instance

There’s nothing to say. You can get the results directly

let p = new Promise((resolve) => {
    setTimeout(resolve, 1000, 1);
})

async function fn() {
    let result = await p;
    console.log('result:', result); // result: 1
}

fn();

5.3.2 await a promise instance of pending

Note that if a promise instance is always in the pending state, thenAwait will always wait for him to change

Namely:The code after await will never execute

let p = new Promise((resolve) => {});

async function fn() {
    Let result = / * the preceding and following code will never execute * / await p;
    console.log('result:', result);
}

fn();

5.3.3 await a rejected project instance

At this point, we will find that the browser will directly report an error, andThe code after await will never be executed again

let p = new Promise((resolve, reject) => {
    reject('error');
})

async function fn() {
    Let result = / * the preceding and following code will never execute * / await p;
    console.log('result:', result);
}

fn();

According to the documentation, we have to use it at this timetry...catch...Error handling

let p = new Promise((resolve, reject) => {
    reject('error');
})

async function fn() {
    try {
        Let result = / * the preceding and following code will never execute * / await p;
        console.log('result:', result);
    } catch (e) {
          //The following sentence will be executed
        console.log(e); 
    }
}

fn();

wait!try...catch...Can’t we just catch the error of synchronization code? For example, the browser will still report an error as follows

try {
    Promise.reject('error');
} catch (e) {
    console.log(e);
}

So why can we catch asynchronous code errors with await now?

Actually, it’s very simple. I’ve explained it before,awaitMake asynchronous code look like synchronous code, sotry...catch...It’s not the fault of grabbing asynchronous code, it’s still the fault of grabbing synchronous code

5.4 await exercise

We are hereSubsection 4.5There are a number of examplesPromise.all()andPromise.race()To demonstrate the final results of these two methods, we usedThen method

Now, we can use itawaitTo rewrite

5.4.1 demo promise. All ()

let p1 = new Promise(() => {});

// pending + reject === reject
Promise.all([p1, Promise.reject('error')])
.then(param => {
    console.log(param)
}, param => {
    console.log(param) // error
})

Can be changed to

async function fn() {
    let p1 = new Promise(() => {});

    // pending + reject === reject
    try {
        let result = await Promise.all([p1, Promise.reject('error')])
        console.log(result);
    } catch (e) {
        console.log(e); // error
    }
}

fn();

Other examples, brothers can practice by themselves, I will not demonstrate

5.4.2 demonstrate promise.race ()

let p1 = new Promise((resolve) => {
    setTimeout(resolve, 1000, 'success');
})

let p2 = new Promise((resolve, reject) => {
    setTimeout(reject, 500, 'error');
})

//P2 ends first, so the result of project. Race() is rejected, passing the rejection reason to then
Promise.race([p1, p2])
.then(param => {
    console.log(param);
}, param => {
    console.log(param); // error
})

Can be changed to

async function fn() {
    let p1 = new Promise((resolve) => {
        setTimeout(resolve, 1000, 'success');
    })

    let p2 = new Promise((resolve, reject) => {
        setTimeout(reject, 500, 'error');
    })

    //P2 ends first, so the result of project. Race() is rejected
    try {
        let result = await Promise.race([p1, p2])
        console.log(result);
    } catch (e) {
        console.log(e); // error
    }
}

fn();

Recommended Today

Looking for frustration 1.0

I believe you have a basic understanding of trust in yesterday’s article. Today we will give a complete introduction to trust. Why choose rust It’s a language that gives everyone the ability to build reliable and efficient software. You can’t write unsafe code here (unsafe block is not in the scope of discussion). Most of […]