Article first

In our jobs and interviews, we often come across these questions: How can we write more beautiful asynchronous code? Do you know promise, async/await? Do you know their order of execution? So let’s go from shallow to deep.

primers

We all know that Javascript works in a single threaded mode, right? So why use this model? It is important to note that the proper place for our page interaction is to manipulate the DOM, in order to avoid the thread synchronization problems that multithreading might cause because of the single-thread working mode.

  • Advantages: Program execution is safer and simpler
  • Disadvantages: easy to get stuck by time-consuming tasks, causing the program to fake death

In this single thread working mode, we have to solve the above problems: synchronous mode, asynchronous mode

Synchronous mode

Most tasks will be executed in abnormal synchronous mode. Tasks will enter the call stack of tasks one at a time and be executed one at a time. After execution, the call stack will be pushed out.

Asynchronous mode

Generally, it is a long time consuming task, which is started immediately after the next task, and its own task is the way of callback function to process the subsequent logic. This is our key to addressing the shortcomings of the single-threaded model.

Promise

The Promise object represents an asynchronous operation with three states:

  • Pending: Indicates the initial state, not the success or failure state.
  • This is a pity: which means that the operation will be completed successfully.
  • Rejected: Indicates that the operation fails.
const promise = new Promise((resolve,reject) => {
   rej(new Error("promise rejected"))
})

promise.then(val => {
   console.log("resolve", val);
}).catch(err => {
   console.log("reject", err);
}) 
console.log("end")
// end
// eject Error: promise rejected
Copy the code

The then method takes a function as an argument, and can be ignored if it is not a function

Simulation of Ajax

function ajax(url) {
    return new Promise((resolve,reject) => {
        let xhr = new XMLHttpRequest()
        xhr.open("get", url)
        xhr.responseType = "json"
        xhr.onload = function() {
            if (this.status === 200) {
                resolve(this.response)
            } else {
                reject(new Error(this.statusText))
            }
        }
        xhr.send()
    })
}
ajax("package.json").then(res => {
    console.log(res)
})
Copy the code

From this, we can see that the essence of promise is to define the tasks that need to be executed after the asynchronous task ends in the way of callback function. However, many students who have just learned to use promise often fall into the mistake of nesting an asynchronous task even after the callback of another asynchronous task ends. This defeats the purpose of using promises. The way to solve this problem is to use the chain call of promise

The chain call to Promise

const promise = new Promise((resolve,reject) => { resolve() }) promise.then(() => { return 1; }).then((res) => { console.log(res); // Output 1}). Then (() => {console.log("3"); })Copy the code
  • PromisethethenMethod returns a brand newPromiseobject
  • At the back of thethenIt’s all about the last onethenThe returnedpromiseAdd a callback after the state is clear
  • In front ofthenThe return value of the callback function in the method followsthenMethod callback parameter
  • If the callback returnsPromiseAnd then the backthenMethod’s callback waits for its completion

Exception handling for promises

Common exception handling

// promise.then(resolve => {console.log("resolve", resolve); }, reject => { console.log("reject", reject); }) // the second promise.then(resolve => {console.log("resolve", resolve); }).catch(err => { console.log("reject", err); })Copy the code

Unhandledrejection \color{red}{unhandledrejection} Unhandledrejection is mounted globally to handle exceptions in code that have not been caught manually. Although this method is provided, it is recommended that all possible exceptions be caught manually

Use in browser:

window.addEventListener("unhandledrejection", event => { const {reason, promise} = event; // Promise => Primose event.preventDefault()},false)Copy the code

Used in node:

Process. on("unhandledRejection", (reason, promise) => {// Reason => promise // Promise => Primose object})Copy the code

A static method for Promise

resolve

Promise.resolve("1").then(val => {console.log(val)}) new Promise((resolve,reject) => {resolve("1")}) //Copy the code
let p1 = new Promise((resolve,reject) => {
    resolve("1")
})
let p2 = Promise.resolve(p1)

console.log(p1 === p2)
// true
Copy the code

Wrapping a Promise object in another Promise object returns the original Promise

A special point is that if the Promise argument is an object containing one of the following THEN’s, it can also be executed as a callback

Promise.resolve({
    then: (onFulfilled,onReject) => {
        onFulfilled("test 1")
    }
}).then(val => {
    console.log(val)
})
// test 1
Copy the code

reject

In the same way

Promise.reject("err").catch(err => console.log(err))
Copy the code

Promise parallel execution

A common scenario is when a page loads and requests multiple interfaces at the same time, but the interfaces are not related to each other. If the sequential requests take a long time, then we need to execute them in parallel. So how do you know when parallel requests have all been returned? So we need to use promise.all ()

Promise.all

Promise.all([promise1, promise2]).then(res => {//res is an array of return values}).catch(err => {// As long as there is a Promise exception})Copy the code

Promise.race

Promise.all([promise1, promise2]). Then (res => {// wait for the first Promise to end})Copy the code

Promise execution timing (macro task, micro task)

NextTick: Promise & MutationObserver & Process.nexttick (node)

Generator asynchronous scheme

The Generator was introduced in ES2015, so let’s review it first

function * foo() { console.log("start") try{ let val = yield "foo" console.log(val) } catch(e) { console.log(e) } } Const generator = foo() // Not immediately executed const result1 = generator.next() // foo() will be executed until the keyword yield is reached console.log(result1) //start //{value: "foo", done: } const result2 = generator.next("bar") Next parameters can be copied to yield console.log(result2) // bar generator.throw(new Error("generator Error "))Copy the code

We can then use this feature in Generator in conjunction with promises

Function * main() {const json = yield ajax("package.json") // console.log(json)} const g = main(); const result = g.next(); result.value.then(data => { g.next(data) })Copy the code

To avoid piling up code with multiple promises, we can take advantage of recursion and do some wrapping

function * main() { const json1 = yield ajax("package.json") console.log(json1) const json2 = yield ajax("package.json")  console.log(json2) const json3 = yield ajax("package.json") console.log(json3) } function co(generator) { const g = main(); function loopHandle(result) { if (result.done) return result.value .then(data => loopHandle(g.next(data)), err => g.throw(err)) } loopHandle(g.next()) } co(main)Copy the code

Aysnc/Await

Aysnc /await is syntactic sugar for generator

async function  main() {
    const json1 = await ajax("package.json")
    console.log(json1)
    const json2 = await ajax("package.json")
    console.log(json2)
    const json3 = await ajax("package.json")
    console.log(json3)
}

main()
Copy the code