Promise, as the mainstream asynchronous solution, is often used at work and in interviews, especially in interviews, where there will be a scene where you write code by hand. Here are five typical questions to familiarize yourself with some of the routines.

Promise brief Introduction

A quick introduction to Promise

The Promise object is used to represent the final completion (or failure) of an asynchronous operation and its resulting value. You can perform functions for the success and failure bindings of asynchronous operations so that asynchronous methods can return values just like synchronous methods, but immediately return a Promise object that represents a possible future outcome.

Promise objects have three states:

  • Pending: The initial state, which is neither successful nor failed.
  • This is a pity: which means that the operation will be completed successfully.
  • Rejected: Indicates that the operation fails.

Static methods used and provided by Promise:

  • new Promise( function(resolve, reject) {... } /* executor */ );: Returns a Promise object
  • Promise.all(iterable)Success is triggered when all the promise objects in the iterable argument succeed. If one fails, a failure is immediately triggered to return the promise object
  • Promise.race(iterable)A success or failure in the iterable argument immediately triggers the success or failure of the returned object
  • Promise.reject(reason): Returns a Promise object in a failed state
  • Promise.resolve(value): Returns a Promise object whose state is given by value, usually used to use a value as a Promise.

So let’s start

an

Combined with the js event loop, write the result as follows

console.log('script start')
async function async1() {
    await async2()
    console.log('async1 end')}async function async2() {console.log('async2 end')}
async1()
setTimeout(function () {console.log('setTimeout')}, 0)
new Promise(resolve= > {
    console.log('Promise')
    resolve()
}).then(function () {
        console.log('promise1')
    }).then(function () {
        console.log('promise2')})console.log('script end')
// The result is as follows
// script start
// async2 end
// Promise
// script end
// async1 end
// promise1
// promise2
// setTimeout
Copy the code

Understanding the loop of events and the Promise. Then () category is a microqueue is a routine.

Question 2

SleepFirst lazyMan(‘ XXX ‘).sleep(1000).eat(‘333’).sleepFirst(2000) sleepFirst

This problem looks at how to combine multiple promises and chain calls.

Functions such as sleep EAT can be temporarily stored in arrays, and in order to be called chained, each function needs to return a Promise object. So when do you execute a function in an array?

According to the event loop mechanism, we use setTimeout to execute the methods in the array. Before we chain the methods in the array, we need to build a Promise object to execute the then methods. A Promise object can be returned via promise.resolve ().

function lazyMan(name) {
    this.task = [];
    this.task.push((a)= > {
        return new Promise(res= > {
            console.log('name:'+ name); res() }) })let run = (a)= > {
        let sequence = Promise.resolve()
        for (const func of this.task) {
            sequence = sequence.then((a)= >func())
        }
    }
    setTimeout((a)= > {run()}, 0)
    this.eat = (str) = > {
        this.task.push((a)= > {
            return new (res= > {
                console.log('eat:'+ str); res() }) })return this;
    }
    this.sleep = (time) = > {
        this.task.push((a)= > {
            return new Promise(res= > {
                setTimeout((a)= > {
                    console.log(`Wake up after `+ time); res() }, time) }) })return this;
    }
    this.sleepFirst = (time) = > {
        this.task.unshift((a)= > {
            return new Promise(res= > {
                setTimeout((a)= > {
                    console.log(`sleepFirst up after `+ time); res() }, time) }) })return this;
    }
    return this;
}
Copy the code

Question 3

Asynchronous tasks can be continuously added to the task queue (all asynchronous tasks are Promise), but only five tasks can be processed at the same time. The next group can be executed only after the execution of five tasks in a group is completed. The execution is suspended when the task queue is empty, and the execution is automatically executed when a new task is added.

class RunQune{
    constructor() {this.list = []; // Task queue
        this.target = 5; // Number of concurrent requests
        this.flag = false; // Task execution status
        this.time = Date.now()
    }
    async sleep(time){
        return new Promise(res= >setTimeout(res,time))
    }
    // Execute the task
    async run(){
        while(this.list.length>0) {this.flag = true;
            let runList = this.list.splice(0.this.target);
            this.time = Date.now()
            await this.runItem(runList)
            await this.sleep(300) // Simulate execution time
        }
        this.flag = false;
    }
    async runItem(list){
        return new Promise((res) = >{
            while(list.length>0) {const fn = list.shift();
                fn().then().finally((a)= >{
                    if(list.length === 0){
                        res()
                    }
                })
            }
        })
    }
    // Add a task
    push(task){
        this.list.push(... task); !this.flag && this.run()
    }
}
Copy the code

This problem can also be further divergent, no need to wait for a group to complete before executing the next group, as long as the number of concurrent tasks is not full, you can add new tasks to execute. The implementation idea is not much changed, finally changed to new tasks.

Is four

Expect ids to print 0, 1, 2, 3, 4 in order, and only the start function can be modified.

function start(id) {
    execute(id)
}
for (let i = 0; i < 5; i++) {
    start(i);
}
function sleep() {
    const duration = Math.floor(Math.random() * 500);
    return new Promise(resolve= > setTimeout(resolve, duration));
}
function execute(id) {
    return sleep().then((a)= > {
        console.log("id", id);
    });
}
Copy the code

The print of the ID is an asynchronous event, executed in the setTimeout callback. According to the above code, whoever finishes the countdown first will print the ID first. If you want to print the ID sequentially, you will need to execute multiple asynchronous events synchronously. The following code

function start(id) {
    // execute(id)
    The execute function returns a promise, so you can take advantage of this by performing the next print via promise.then
    this.promise = this.promise ? this.promise.then((a)= >execute(id)) : execute(id)

    // The second method is essentially the same as the first method: first use an array to store asynchronous functions, and then use the next stage of the event loop, the setTimeout callback, to execute the chained call to promise
    this.list = this.list ? this.list : []
    this.list.push((a)= > execute(id))
    this.t;
    if (this.t) clearTimeout(this.t)
    this.t = setTimeout((a)= > {
        this.list.reduce((re, fn) = > re.then((a)= > fn()), Promise.resolve())
    })

    // The array stores the id value and executes the execute function asynchronously with await
    this.list = this.list ? this.list : []
    this.list.push(id)
    clearTimeout(this.t)
    this.t = setTimeout(async() = > {let _id = this.list.shift()
        while(_id ! = =undefined) {
            await execute(_id);
            _id = this.list.shift()
        }
    })
}
Copy the code

Topic five

Hand-torn source code series, to handwritten A Promise, before starting to understand the Promise/A+ specification, list the key parts of the specification, detailed specification can be seen at the end of the link

  1. Promise state: The current state of a Promise must be one of the following three states: Pending, Fulfilled and Rejected.
  2. State migration: Wait state can be migrated to execute state or reject state. The execution and rejection states cannot be migrated to other states and must have an immutable final value
  3. Then methods: A promise must provide a THEN method to access its current value, final value, and reason. Then methods can be called multiple times by the same promise. The then method takes two argumentsonFulfilled, onRejected, onFulfilled and onRejected must be called as functions and cannot be called more than once. The then method returns a Promise object

I implemented a simplified version of the Promise based on these three points

function MPromise(executor) {
    this.status = 'pending'; // Pending, pity, rejected
    this.data = ' ' // The current promise value is mainly used in the then method, which is a pity and rejected
    this.resolveFuncList = []; // The reason for using arrays is that a promise can execute multiple THEN methods at the same time, hence multiple THEN callbacks
    this.rejectFunc;
    const self = this;
    function resolve(value) {
        // Use setTimeout to implement asynchrony
        setTimeout((a)= > {
            if (self.status === 'pending') {
                self.status = 'fulfilled';
                self.data = value;
                // Execute resolve
                self.resolveFuncList.forEach(func= >{ func(value) }); }})}function reject(reason) {
        setTimeout((a)= > {
            if (self.status === 'pending') {
                self.status = 'rejected'; self.data = value; self.rejectFunc && self.rejectFunc(reason); }})}try {
        executor(resolve, reject)
    } catch (error) {
        reject(error)
    }
}

MPromise.prototype.then = function (onFulfilled, onRejected) {
    let promise2;
    // Distinguish between different states of processing
    if (this.status === 'pending') {
        return promise2 = new MPromise((res, rej) = > {
            this.resolveFuncList.push(function (value) {
                let x = onFulfilled(value);
                resolvePromise(promise2, x, res, rej)
            })

            this.rejectFunc = function (reason) {
                let x = onRejected(reason);
                resolvePromise(promise2, x, res, rej)
            }
        })
    }
    if (this.status === 'fulfilled') {
        return promise2 = new MPromise((res, rej) = > {
            setTimeout((a)= > {
                let x = onFulfilled(this.data) // Outputs the result of the last execution
                resolvePromise(promise2, x, res, rej)
            })
        })
    }
    if (this.status === 'rejected') {
        return promise2 = new MPromise((res, rej) = > {
            setTimeout((a)= > {
                let x = onRejected(this.data)
                resolvePromise(promise2, x, res, rej)
            })
        })
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (x instanceof MPromise) {
        if (x.status === 'pending') {
            x.then(value= > {
                resolvePromise(promise2, value, resolve, reject)
            }, reason => {
                reject(reason)
            })
        } else {
            x.then(resolve, reject)
        }
    } else {
        resolve(x)
    }
}
Copy the code

The following two are frequently asked about apis that will allow handwritten promises due to time constraints

Realize the Promise. All

/** * Promise. All Promise is processed in parallel * Parameters: array of Promise objects as parameters * return value: This is a big pity. Return a Promise instance * This will be FulFilled when all the Promise objects in this array enter the FulFilled state. * /
Promise.all = function(promises) {
    return new Promise((resolve, reject) = > {
        let values = []
        let count = 0
        promises.forEach((promise, index) = > {
            promise.then(value= > {
                console.log('value:', value, 'index:', index)
                values[index] = value
                count++
                if (count === promises.length) {
                    resolve(values)
                }
            }, reject)
        })
    })
}
Copy the code

Realize the Promise. Rase

/** * promise.race * argument: accepts an array of Promise objects as arguments * returns a value: Return a Promise instance * As long as one of the Promise objects enters the FulFilled or Rejected state, the subsequent processing will continue (depending on which is faster) */
Promise.race = function(promises) {
    return new Promise((resolve, reject) = > {
        promises.forEach((promise) = > {
            promise.then(resolve, reject);
        });
    });
}
Copy the code

summary

Promise’s SAO operation is still very much, welcome friends in the comments section to share your problems (tao) items (lu).

Refer to the article

www.ituring.com.cn/article/665…

promisesaplus.com/