33 concepts JavaScript developers should understand 12-Promise, async and wait

33 concepts JavaScript developers should understand 12-Promise, async and wait

Directory

  1. call stack
  2. primitive type
  3. Value types and reference types
  4. Implicit, explicit, nominal and duck typing
  5. == and ===, typeof and instanceof
  6. this, call, apply and bind
  7. Function scope, block scope and lexical scope
  8. Closure
  9. Higher order functions such as map, reduce, filter etc.
  10. expressions and statements
  11. variable promotion
  12. Promise async and wait
  13. Immediate function execution, modularity, namespace
  14. recursion
  15. algorithm
  16. data structure
  17. Message queue and event loop
  18. setTimeout, setInterval and requestAnimationFrame
  19. Inheritance, polymorphism and code reuse
  20. Bitwise operators, array-like objects and typed arrays
  21. DOM tree and rendering process
  22. new and constructor, instanceof and instance
  23. Prototype inheritance and prototype chain
  24. Object.create and Object.assign
  25. Factory functions and classes
  26. Design Patterns
  27. Memoization
  28. Pure functions, function side effects and state changes
  29. Performance-consuming operations and time complexity
  30. JavaScript engine
  31. Binary, decimal, hexadecimal, scientific notation
  32. Partial functions, currying, Compose and Pipe
  33. Code cleanliness

Introduction

Record a process of learning JavaScript. The article is not written in order. The directory link will be updated after it is written. The directory of this article was created with reference to @leonardomso. The English version project address is here

Foreword

This article is divided into four parts

  1. Introduction and use of promises
  2. handwritten promise
  3. Using promises in interview questions and projects
  4. async and wait

1.Introduction and use of promise

1.1 Introduction

A Promise object represents the final completion (or failure) of an asynchronous operation and its result value. A Promise is a proxy , which represents a value that is not necessarily known when the promise is created. It allows you to associate a handler with the final success value or failure reason of an asynchronous operation. This allows an asynchronous method to return a value just like a synchronous method: instead of returning the final value immediately, an asynchronous method returns a promise to provide that value at some point in the future.

A Promise must be in one of the following states:

  • Pending: Initial status, neither honored nor rejected.
  • Fulfilled: means the operation was completed successfully.
  • Rejected: means the operation failed.

Once a Promise is resolved or rejected, it cannot be migrated to any other state (i.e. state immutable)

look at an example

let p1 = new Promise((resolve, reject) => {<!-- -->
    resolve('success')
    reject('failed')
})
console.log('p1', p1)
let p2 = new Promise((resolve, reject) => {<!-- -->
    reject('failed')
    resolve('success')
})
console.log('p2', p2)

image-20231017105720424

Promise is only subject to the first time. If it succeeds for the first time, it will be permanently as fulfilled. If it fails for the first time, the status will always be . rejected

Can be moved to any other state

1.2.Problems solved by Promise

Before Promise appeared, we handled multiple asynchronous network requests, something like this

Request 1(function(request result 1){<!-- -->
    Request 2(function(request result 2){<!-- -->
        Request 3(function(request result 3){<!-- -->
            Request 4(function(request result 4){<!-- -->
                Request 5(function(request result 5){<!-- -->
                    Request 6(function(request result 3){<!-- -->
                        ...
                    })
                })
            })
        })
    })
})

With promise

new Promise(request 1)
    .then(request 2(request result 1))
    .then(request 3(request result 2))
    .then(request 4(request result 3))
    .then(request 5(request result 4))
    .catch(Exception handling (exception information))

Summarized as follows

  • A promise may have three states: pending, fulfilled, and rejected.

  • The state of a promise can only be changed from “waiting” to “complete” or “rejected”, and cannot be converted in the reverse direction. At the same time, the “completed” state and “rejected” state cannot be converted to each other.

  • Promise must implement the then method (it can be said that then is the core of promise), and then must return a promise. Then of the same promise can be called multiple times, and the execution order of callbacks is the same as when they were defined. in the same order

  • The then method accepts two parameters. The first parameter is a callback on success, which is called when the promise transitions from the “waiting” state to the “completion” state. The other is a callback on failure, which is called when the promise transitions from the “waiting” state to the “completion” state. Called when in “reject” state. At the same time, then can accept another promise passed in, and also accept an object or method “like then”, that is, a thenable object.

  • Promise is a solution for asynchronous programming that can solve the problem of asynchronous callback hell and make asynchronous programming clearer and more concise. In traditional asynchronous programming, if there are multiple asynchronous tasks, each asynchronous task needs to be completed after the previous asynchronous task is completed. This will form nested callback functions, making the code difficult to maintain and read. Promise can execute multiple asynchronous tasks serially or in parallel, and can uniformly handle the success or failure status of asynchronous tasks.

    When using Promise, multiple asynchronous tasks can be connected in series through chain calls, avoiding the problem of nested callback functions. At the same time, Promise also provides a unified catch method, which can capture errors in all Promise chains and handle exceptions more conveniently.

    Therefore, Promise solves the readability and maintainability problems of asynchronous programming and improves the quality of code and the efficiency of development.

1.4 Use of Promise

Basic usage

let p = new Promise((resolve, reject) => {<!-- -->
    //Do some asynchronous operations
    setTimeout(() => {<!-- -->
        console.log('Execution completed');
        resolve('I am successful!!');
    }, 2000);
});

Usage of then

const p1 = new Promise((resolve, reject) => {<!-- -->
    resolve('success 1')
}).then(res => {<!-- -->
    console.log(res)
}).catch(error => {<!-- -->
    console.log(error);
})
//Input successful 1
//It can also be abbreviated as follows
const p1 = new Promise((resolve, reject) => {<!-- -->
    resolve('success 1')
}).then(res => console.log(res), err => console.log(err))

then there is a timer situation

const p1 = new Promise((resolve, reject) => {<!-- -->
    setTimeout(() => {<!-- -->
        resolve('success 1')
    }, 2000)
}).then(res => {<!-- -->
    console.log(res)
}).catch(error => {<!-- -->
    console.log(error);
})
//Output successful 1 after 2 seconds

catch

const p1 = new Promise((resolve, reject) => {<!-- -->
    reject('Failed 1')
}).then(res => {<!-- -->
    console.log(res)
}).catch(error => {<!-- -->
    console.log(error);
})
//Output failed 1

As can be seen from the above example

The created promise will eventually end with resolved status (fulfilled) or rejected status (rejected), And call the corresponding callback function (passed to then and catch) when completed.

then.Chain call The next then execution is affected by the return value of the previous then The then method itself will return a new Promise object

const p1 = new Promise((resolve, reject) => {<!-- -->
    resolve(100)
}).then(res => {<!-- -->
    return res + 1
}).then(res=>{<!-- -->
    console.log(res);
})
//Output 101

then is a micro task

const p1 = new Promise((resolve, reject) => {<!-- -->
    resolve('1')
}).then(res => {<!-- -->
    console.log(res)
}).catch(error => {<!-- -->
    console.log(error);
})
console.log(2)
//Output sequence 2, 1

promise method

Method name Description
Promise.all([Promise1, Promise2]) All successes are successful, If one fails, the failed one will be returned Non-Promise items will be returned successfully
Promise.any([ Promise1, Promise2]) Contrary to all, if both fail, an error will be reported If one succeeds, one will return the successful one If it is not a Promise item, then this item will be regarded as successful
Promise.allSettled([Promise1, Promise2]) Collect the results of each Promise into an array and return
Promise .race([Promise1, Promise2]) Which Promise gets the result the fastest, that result will be returned, regardless of success or failure

2.Handwritten promise

1.1 Structure

The native promise is written like this

const p1 = new Promise((resolve, reject)=>{<!-- -->})

It can be seen that the native promise can be used by new to execute the resolve and reject methods by itself.

So we write like this: define a Mypromise and use the constructor to execute the resolve and reject methods

class Mypromise{<!-- -->
    constructor(func) {<!-- -->
       func(this.resolve,this.reject())
    }
    resolve(){<!-- -->}
    reject(){<!-- -->}
}
// Constructor is a special method in JavaScript that is automatically called when creating a new object instance. The constructor method is executed when a class is instantiated and is used to initialize the property values of the instance.

There are three states of promise: pending (pending), fulfilled (success), rejected (failed). The default state is pending, and then when executed, the state becomes fulfilled or rejected, fulfilled or rejected. Either fulfilled or rejected of the promise can pass in a parameter.

we can write like this

class Mypromise {<!-- -->
    static PENDING = 'Pending';
    static FULFILLED = "Success";
    static REJECTED = "Rejected"
    constructor(func) {<!-- -->
        this.status = Mypromise.PENDING; //The default status is pending
        this.result = null;
        func(this.resolve, this.reject())
    }
    resolve(result) {<!-- -->
        if (this.status === Mypromise.PENDING) {<!-- -->
            this.status = Mypromise.FULFILLED
            this.result = result
        }
    }
    reject(result) {<!-- -->
        if (this.status === Mypromise.PENDING) {<!-- -->
            this.status = Mypromise.REJECTED
            this.result = result
        }

    }
}

Knocking on the blackboard, all properties and methods modified by static are static methods and properties. They can only be called by class names and cannot be called by instantiated objects. At the same time, they cannot be inherited by subclasses. In other words, they belong to the current one. Class.

then method

class Mypromise {<!-- -->
    static PENDING = 'Pending';
    static FULFILLED = "Success";
    static REJECTED = "Rejected"
    constructor(func) {<!-- -->
        this.status = Mypromise.PENDING; //The default status is pending
        this.result = null;
        func(this.resolve.bind(this), this.reject.bind(this))
        //bind Bind this to the resolve method of the instance as the current instance object, and execute the resolve method
    }
    resolve(result) {<!-- -->
        if (this.status === Mypromise.PENDING) {<!-- -->
            this.status = Mypromise.FULFILLED
            this.result = result
        }
    }
    reject(result) {<!-- -->
        if (this.status === Mypromise.PENDING) {<!-- -->
            this.status = Mypromise.REJECTED
            this.result = result
        }

    }
    then(onFULFILLED,onREJECTED){<!-- -->
        if (this.status === Mypromise.FULFILLED) {<!-- -->
            onFULFILLED(this.result)
        }
        if (this.status === Mypromise.REJECTED) {<!-- -->
            onREJECTED(this.result)
        }
    }
}
let p1 = new Mypromise((resolve,reject)=>{<!-- -->
    resolve("success")
})
p1.then(
    result => {<!-- -->
        console.log(result)
    },
    result => {<!-- -->
        console.log(result.message)
    }
)
//output successful

Use try catch to throw an exception and determine whether the function is empty

class Mypromise {<!-- -->
    static PENDING = 'Pending';
    static FULFILLED = "Success";
    static REJECTED = "Rejected"
    constructor(func) {<!-- -->
        this.status = Mypromise.PENDING; //The default status is pending
        this.result = null;
        try{<!-- -->
            func(this.resolve.bind(this), this.reject.bind(this))
        }catch (error){<!-- -->
            this.reject(error)
        }
        //bind Bind this to the resolve method of the instance as the current instance object, and execute the resolve method
    }
    resolve(result) {<!-- -->
        if (this.status === Mypromise.PENDING) {<!-- -->
            this.status = Mypromise.FULFILLED
            this.result = result
        }
    }
    reject(result) {<!-- -->
        if (this.status === Mypromise.PENDING) {<!-- -->
            this.status = Mypromise.REJECTED
            this.result = result
        }

    }
    then(onFULFILLED,onREJECTED){<!-- -->
        onFULFILLED = typeof onFULFILLED === "function" ? onFULFILLED :() =>{<!-- -->};
        onREJECTED = typeof onREJECTED === "function" ? onREJECTED :() =>{<!-- -->};
        if (this.status === Mypromise.FULFILLED) {<!-- -->
            onFULFILLED(this.result)
        }
        if (this.status === Mypromise.REJECTED) {<!-- -->
            onREJECTED(this.result)
        }
    }
}
let p1 = new Mypromise((resolve,reject)=>{<!-- -->
    resolve("success")
})
p1.then(
    result => {<!-- -->
        console.log(result)
    },
    result => {<!-- -->
        console.log(result.message)
    }
)

3.Interview questions

Question 1

const promise = new Promise((resolve, reject) => {<!-- -->
    console.log(1);
    resolve();
    console.log(2);
    reject('error');
})
promise.then(() => {<!-- -->
    console.log(3);
}).catch(e => console.log(e))
console.log(4);

Rule 1, the code in the promise constructor will be executed immediately, and the code in then or reject will be put into the asynchronous microtask queue, and in the macrotask It will be executed immediately after completion. Rule 2: Once the status of promise changes to success or failure, it will not change again, so the execution result is: 1,2,4,3. The functions in catch will not be executed again.

Question 2

const promise = new Promise((resolve, reject) => {<!-- -->
        setTimeout(() => {<!-- -->
             console.log('once')
             resolve('success')
        }, 1000)
 })
promise.then((res) => {<!-- -->
       console.log(res)
     })
promise.then((res) => {<!-- -->
     console.log(res)
 })

The constructor of promise will only be executed once, while the then method can be called multiple times, but the second time it returns the result directly, there will be no asynchronous waiting time, so execution The result is: Print after one second: once,success,success.

Question three

const p1 = () => (new Promise((resolve, reject) => {<!-- -->
    console.log(1);
    let p2 = new Promise((resolve, reject) => {<!-- -->
        console.log(2);
        const timeOut1 = setTimeout(() => {<!-- -->
            console.log(3);
            resolve(4);
        }, 0)
        resolve(5);
    });
    resolve(6);
    p2.then((arg) => {<!-- -->
        console.log(arg);
    });

}));
const timeOut2 = setTimeout(() => {<!-- -->
    console.log(8);
    const p3 = new Promise(reject => {<!-- -->
        reject(9);
    }).then(res => {<!-- -->
        console.log(res)
    })
}, 0)


p1().then((arg) => {<!-- -->
    console.log(arg);
});
console.log(10);

From the perspective of code execution sequence, the program initially executes the code in the order of the code. When it encounters a synchronous task, it is executed immediately; when it encounters an asynchronous task, it just calls an asynchronous function to initiate an asynchronous request. At this point, the asynchronous task starts to perform the asynchronous operation, and after the execution is completed, it is queued in the message queue. After the program is executed according to the code sequence, check whether there are any waiting messages in the message queue. If so, put the messages from the message queue into the execution stack in order. After the execution is completed, the message is obtained from the message queue, executed again, and repeated.

Event loop: There is an event loop EventLoot*p rule in the execution rules of javascript. In the event loop, asynchronous events will be placed in the asynchronous queue, but the asynchronous queue is divided into macro tasks And microtasks, browser-side macrotasks generally include: script tag, setTimeout, setInterval, setImmediate, requestAnimationFrame. Microtasks include: MutationObserver,Promise.then catch finally. Macro tasks will block the browser’s rendering process, and micro tasks will be executed immediately after the macro task ends, before rendering.

The result of the above question is **: 1,2,10,5,6,8,9,3’**

  1. Step 1: The Promise constructor will be executed immediately and the code will be executed in order. Output 1, 2, 10. At this time, there are asynchronous tasks in the event loop and placed in the asynchronous queue.
  2. Step 2 The asynchronous queue is divided into macro tasks and micro tasks. Then execute the micro tasks first and then the macro tasks. Micro tasks include: p2.then, p1().thenMacro tasks include: timeOut1 timeOut2. Select Execute microtask Output 5,6
  3. Step 3: After the microtask is executed, the macrotask is executed: timeOut1 timeOut2. The actual setTimeout stack sequence is timeOut2>timeOut1. Because setTimeout2 is added to the macro asynchronous task after the promise is executed, it is queued at the back. Execute timeOut2 and output 8, during the execution of the macro task, the p3.then microtask enters the queue. After the macrotask is executed, the microtask will be executed. Output: 9 Then timeOut1 is executed, Output: 3
  4. The constructor of promise will only be executed once and 4 will not be printed.

4.async and wait

  • async/await Execute asynchronous tasks in a synchronous manner
  • async declares that the function is asynchronous and the function will return a promise.
  • await must be used in async functions

1.How does the async function handle its return value? Look at the example below

async function test() {<!-- -->
return 'hello async';
}
let result = test();
console.log(result);

Printed results

image-20231102150658847

The async function returns a Promise object

What if the async function does not return a value?

image-20231102151025067

It returns Promise.resolve(undefined).

2.await What are you waiting for? Look at the example below

function getSomething(){<!-- -->
    return "something";
}
async function testAsync(){<!-- -->
    return Promise.resolve('hello async');
}
async function test(){<!-- -->
    let v1 = await getSomething();
    let v2 = await testAsync();
    console.log(v1,v2);
}
test();
console.log('I executed');
//The execution result is:
//I executed
//something,hello async

in conclusion

1. If it is not waiting for a Promise object, the result of the await expression is what it is waiting for.

2. If it is waiting for a Promise object, await will be busy, waiting for the Promise object to resolve, and then get the value of resolve as the result of the await expression.

3.await + function

function fn() {<!-- -->
    console.log('fn start')
    console.log('fn end')
}
async function run() {<!-- -->
    console.log('start 1')
    const res = await fn()
    console.log("111",res)
    console.log('end')
}
run()
console.log('3')

Results of the

image-20231102153036046

Conclusion: If the right side of await is a function, it will execute this function immediately, and only after the execution of this function ends (that is, the function is completed)! Only then will the remaining async tasks be pushed into the microtask queue

When await is encountered, the code behind it (not the entire code) inside the function will be blocked to execute the synchronization code outside the function; when the external synchronization code is completed, return This function executes the remaining code. And when await is executed, the code of the microtask queue will be processed first

  1. await plus async function

    look at an example

    function funOne() {
         return ("Execute the first function");
     }
     function funTwo() {
         $.ajax({
             url:'./data.json',
             success: (res) => {
                 return("Execute the second callback function");
             }
         })
     }
     function funThree() {
         return ("Execute the third function");
     }
     function run() {
         console.log(funOne());
         console.log(funTwo());
         console.log(funThree());
     }
     run()
    

    Print results

    • Execute the first function
    • Execute the third function
    • Execute the second callback function

We add an asynchronous method

function funOne() {
        return ("Execute the first function");
    }
    function funTwo() {
        return new Promise((resolve,reject) => {
            resolve("Execute the second callback function");
        }).then(res => {
            console.log(res)
        })
    }
    function funThree() {
        return ("Execute the third function");
    }
    function run() {
        console.log(funOne());
        console.log(funTwo());
        console.log(funThree());
    }
    run()

Print results

  • Execute the first function
  • Execute the third callback function
  • Execute the second function

Use await and async to transform it

function funOne() {<!-- -->
    return ("Execute the first function");
}
function funTwo() {<!-- -->
    return new Promise((resolve,reject) => {<!-- -->
        resolve("Execute the second callback function");
    }).then(res => {<!-- -->
        console.log("1111",res)
    })
}
function funThree() {<!-- -->
    return ("Execute the third function");
}
async function run() {<!-- -->
    console.log(funOne());
    console.log(await funTwo());
    console.log(funThree());
}
run()

Print results

  • Execute the first function
  • Execute the third function
  • Execute the second callback function
syntaxbug.com © 2021 All Rights Reserved.