Interviewer: Write a promise by hand. Me: I don’t quite understand can use can’t write…

Find the pain point and make it happen…

The use of the promise

Const text = new Promise((resolve, reject) => {resolve(" I'm good "); }); text.then((res) => { console.log(res) }, (err) => { console.log(err) });Copy the code

The realization of the promise

Promise is a class, so let’s use class to define it


class TestPromise {
    constructor() {}
} 

Copy the code

As you know, promise has three states, so let’s define it and give it an initial state,

const RESOLVE = 'resolve',
const REJECT = 'reject',
const PENDING = 'pending',


class TestPromise {
    status = PENDING;
    constructor() {}
}
Copy the code

A promise takes a function as an argument and executes immediately after it is passed in

constructor(excution) {
        excution()
    }
Copy the code

The function takes resolve,reject, so we’ll define it

const RESOLVE = 'resolve', const REJECT = 'reject', const PENDING = 'pending', class TestPromise { status = PENDING; result = undefined; reason = undefined; Constructor (excution) {// Call resolve and reject. Default: undefined const resolve = (result) => {} const reject = (reason) => {} excution(resolve, reject)}}Copy the code

We know that the promise state cannot be changed once it changes, so we need to change the corresponding state when entering the corresponding function

const resolve = (result) => { if(this.status === PENDING) { this.result = result; this.status = RESOLVE; } } const reject = (reason) => { if(this.status === PENDING) { this.result = reason; this.status = REJECT; }}Copy the code

And then we call the then method, and we define it in the code

// Call the then method to receive two methods! then(onResolved, onReject) { }Copy the code

The method passed in needs to be run when the first parameter and the second parameter are in different states

// Call the then method to receive two methods! Then (onResolved, onReject) {// The incoming method needs to run, If (this.status === RESOLVE) {onResolved(this.result)} if (this.status === REJECT) { onReject(this.result) } }Copy the code

The first edition is almost out. It looks like this

const RESOLVE = 'resolve', const REJECT = 'reject', const PENDING = 'pending', class TestPromise { status = PENDING; result = undefined; reason = undefined; Constructor (excution) {// Call resolve and reject. Const resolve = (result) => {if(this.status === PENDING) {this.result = result; this.status = RESOLVE; } } const reject = (reason) => { if(this.status === PENDING) { this.result = reason; this.status = REJECT; } } excution(resolve, reject); } // Call the then method and receive two methods! Then (onResolved, onReject) {// The incoming method needs to run, If (this.status === RESOLVE) {onResolved(this.result)} if (this.status === REJECT) { onReject(this.result) } } }Copy the code

verify

Const newTest = new TestPromise((resolve, reject) => {resolve(' resolve, reject '); }); Const text = new Promise((resolve, reject) => {resolve(" I'm good "); }); text.then((res) => { console.log(res) }, (err) => { console.log(err) }); newTest.then((res) => { console.log(res); }, (err) => {console.log(err)})Copy the code

The print-out came out, but it felt like something was wrong. My promise wasn’t asynchronous… Let’s do that. Modify the method in then

Then (onResolved, onReject) {// The incoming method needs to run, If (this.status === RESOLVE) {setTimeout(() => {onResolved(this.result)}, 0) } if (this.status === REJECT) { setTimeout(() => { onReject(this.result) }) } }Copy the code

Ok! To achieve! Since the native Promise was a microservice provided by the V8 engine, we could not implement the V8 engine service, so we used setTimeout to implement asynchro

The asynchronous call

Now that we’ve implemented a simple promise, what happens if we pass in an asynchronous operation

Const newTest = new TestPromise((resolve, reject) => {setTimeout(() => {resolve(' resolve, reject ')); }, 1000)}); newTest.then((res) => { console.log(res); }, (err) => {console.log(err)})Copy the code

You can see that no results are returned from the console

If the then method is called and the state is pending, we need to store the successful and failed callbacks separately. Resolve or Reject is triggered when an asynchronous task executes, calling successful and failed callbacks in sequence

Improve the code

const RESOLVE = 'resolve'; const REJECT = 'reject'; const PENDING = 'pending'; class TestPromise { status = PENDING; result = undefined; reason = undefined; OnResolveCallbackArr = []; onResolveCallbackArr = []; OnRejectCallbackArr = [] constructor(excution) { Const resolve = (result) => {if(this.status === PENDING) {this.result = result; this.status = RESOLVE; / / to iterate through group perform this operation. OnResolveCallbackArr. ForEach (fn = > fn ()); } } const reject = (reason) => { if(this.status === PENDING) { this.result = reason; this.status = REJECT; / / traverse performed on this data. OnRejectCallbackArr. ForEach (fn = > fn ()); } } excution(resolve, reject); } // Call the then method and receive two methods! Then (onResolved, onReject) {// The incoming method needs to run, If (this.status === RESOLVE) {setTimeout(() => {onResolved(this.result)}, 0) } if (this.status === REJECT) { setTimeout(() => { onReject(this.result) }, This. Status === PENDING) {// If (this. Status === PENDING) {// This is a big pity. And in turn the corresponding function enclosing onResolveCallbackArr. Push (() = > {setTimeout (() = > {onResolved (enclosing the result)}, 0)}); this.onRejectCallbackArr.push(() => { setTimeout(() => { onReject(this.result) }, 0)})}} const newTest = new TestPromise((resolve, reject) => {setTimeout(() => {resolve(' resolve ')); }, 1000)}); newTest.then((res) => { console.log(res); }, (err) => {console.log(err)})Copy the code

This completes the asynchronous approach.

We do this asynchronously by using the publish-subscribe model, which is a process of collecting dependencies -> triggering notifications -> extracting dependencies.

Chain calls

Promise can support chained calls so our dot then every time we return a promise, let’s do that

Then (onResolved, onReject) {let promiseTwo = new TestPromise((resove, reject) => { If (this.status === RESOLVE) {setTimeout(() => {onResolved(this.result)}, 0) } if (this.status === REJECT) { setTimeout(() => { onReject(this.result) }, This. Status === PENDING) {// If (this. Status === PENDING) {// This is a big pity. And in turn the corresponding function enclosing onResolveCallbackArr. Push (() = > {setTimeout (() = > {onResolved (enclosing the result)}, 0)}); this.onRejectCallbackArr.push(() => { setTimeout(() => { onReject(this.result) }, 0)})}}) // Return a promise return promiseTwo; }Copy the code

Then test it out.

Const newTest = new TestPromise((resolve, reject) => {setTimeout(() => {resolve(' resolve, reject ')); }, 1000)}); newTest.then((res) => { console.log(res); (err) = >}, {the console. The log (err)}), then ((res) = > {the console. The log (res, 'ha ha ha ha')}, (err) = > {the console. The log (err)})Copy the code

The second chained call is not printed, and although we have returned a promise, the resolve in the promise is not raised… So let’s write a way for him to trigger the event

const RESOLVE = 'resolve'; const REJECT = 'reject'; const PENDING = 'pending'; const hendlePromise = (result, newPromise, resolve, If (result === newPromise) {throw new Error('can not return oneself')} // If ((typeof result === 'object' && typeof result! = = null) | | typeof result = = = 'function') {/ / see how yao has then method const then = result. Then; // If there is a then method and it is a function, we consider it a promise,  if (typeof then === 'function') { then.call(result, (r) => { hendlePromise(r, newPromise, resolve, reject) }, (e) => { reject(e) }) } else { resolve(result); } } else { resolve(result) } } class TestPromise { status = PENDING; result = undefined; reason = undefined; OnResolveCallbackArr = []; onResolveCallbackArr = []; OnRejectCallbackArr = [] constructor(excution) { Const resolve = (result) => {if(this.status === PENDING) {this.result = result; this.status = RESOLVE; / / to iterate through group perform this operation. OnResolveCallbackArr. ForEach (fn = > fn ()); } } const reject = (reason) => { if(this.status === PENDING) { this.result = reason; this.status = REJECT; / / traverse performed on this data. OnRejectCallbackArr. ForEach (fn = > fn ()); } } excution(resolve, reject); } // Call the then method and receive two methods! Then (onResolved, onReject) {let promiseTwo = new TestPromise((resove, reject) => { If (this.status === RESOLVE) {setTimeout(() => {const t = onResolved(this.result) hendlePromise(t, promiseTwo, resove, reject) }, 0) } if (this.status === REJECT) { setTimeout(() => { const t = onReject(this.result) hendlePromise(t, promiseTwo, Resove, reject)}, 0)} if (this. Status === PENDING) {// This. After the wait state is determined, And in turn the corresponding function enclosing onResolveCallbackArr. Push ((() = > {setTimeout () = > {const t = onResolved. This result hendlePromise (t,  promiseTwo, resove, reject) }, 0) }); this.onRejectCallbackArr.push(() => { setTimeout(() => { const t = onReject(this.result) hendlePromise(t, promiseTwo, Resove, reject)}, 0)}) // Return a promise return promiseTwo; }} const newTest = new TestPromise((resolve, reject) => {setTimeout(() => {resolve(' resolve, reject ')); }, 1000)}); newTest.then((res) => { console.log(res); Return 'complete'}, (err) = > {the console. The log (err)}), then ((res) = > {the console. The log (res, 'ha ha ha ha')}, (err) = > {the console. The log (err)})Copy the code

Let’s look at the results

So what we’re left with is we need to add a try catch method to our code in case something goes wrong — look at the whole code

const RESOLVE = 'resolve'; const REJECT = 'reject'; const PENDING = 'pending'; const hendlePromise = (result, newPromise, resolve, Reject) => {if (result === newPromise) {throw new Error('can not return oneself')} Ensure that the state changes only once let lock; If ((typeof result === 'object' && typeof result! = = null) | | typeof result = = = 'function') {try {/ / see how yao has then method const then = result. Then; If (typeof then === 'function') {then. Call (result, (r) => {if (lock) return; if (typeof then === 'function') {then. lock = true; hendlePromise(r, newPromise, resolve, reject) }, (e) => { if (lock) return; lock = true; reject(e) }) } else { resolve(result); } } catch (error) { if (lock) return; lock = true; reject(error) } } else { resolve(result) } } class TestPromise { status = PENDING; result = undefined; reason = undefined; OnResolveCallbackArr = []; onResolveCallbackArr = []; OnRejectCallbackArr = [] constructor(excution) { Const resolve = (result) => {if(this.status === PENDING) {this.result = result; this.status = RESOLVE; / / to iterate through group perform this operation. OnResolveCallbackArr. ForEach (fn = > fn ()); } } const reject = (reason) => { if(this.status === PENDING) { this.result = reason; this.status = REJECT; / / traverse performed on this data. OnRejectCallbackArr. ForEach (fn = > fn ()); } } try { excution(resolve, reject); } catch (error) {reject(error)}} Then (onResolved, onReject) {let promiseTwo = new TestPromise((resove, reject) => { If (this.status === RESOLVE) {setTimeout(() => {try {const t = onResolved(this.result) hendlePromise(t, promiseTwo, resove, reject) } catch (error) { reject(error) } }, 0) } if (this.status === REJECT) { setTimeout(() => { try { const t = onReject(this.result) hendlePromise(t, promiseTwo, resove, reject) } catch (error) { reject(error) } }, This. Status === PENDING) {// If (this. Status === PENDING) {// This is a big pity. And in turn the corresponding function enclosing onResolveCallbackArr. Push ((() = > {setTimeout () = > {try t = {const onResolved (enclosing the result) hendlePromise(t, promiseTwo, resove, reject) } catch (error) { reject(error) } }, 0) }); this.onRejectCallbackArr.push(() => { setTimeout(() => { try { const t = onReject(this.result) hendlePromise(t, PromiseTwo, resove, reject)} Catch (error) {reject(error)}}, 0)})}) // Return a promise return promiseTwo; }} const newTest = new TestPromise((resolve, reject) => {setTimeout(() => {resolve(' resolve, reject ')); }, 1000)}); newTest.then((res) => { console.log(res); Return 'complete'}, (err) = > {the console. The log (err)}), then ((res) = > {the console. The log (res, 'ha ha ha ha')}, (err) = > {the console. The log (err)})Copy the code

promiseAPI

catch

catch(errCallback) {
    return this.then(null, errCallback)
}
Copy the code

resolve

static resolve(data) {
        return new TestPromise((resolve, reject) => {
            if (data instanceof TestPromise) {
                return data.then(resolve, reject)
            }
            if (this.status === PENDING) {
                this.status = RESOLVE;
                this.reason = data;
                this.onResolveCallbackArr.forEach(fn => fn())
            }
            resolve(data)
        })
    }
Copy the code

reject

static reject(reason){ return new TestPromise((resolve,reject)=>{ reject(reason); })}Copy the code

all

static all(values) { return new TestPromise((resolve, reject) => { let resultArr = []; let orderIndex = 0; const processResultByKey = (value, index) => { resultArr[index] = value; if (++orderIndex === values.length) { resolve(resultArr) } } for (let i = 0; i < values.length; i++) { let value = values[i]; if (value && typeof value.then === 'function') { value.then((value) => { processResultByKey(value, i); }, reject); } else { processResultByKey(value, i); }}})}Copy the code

race

static rece(values) {
    return new TestPromise((resove, reject) => {
        values.forEach((item) => {
            if (item && typeof item.then === 'function') {
                item.then(resove, reject)
            } else {
                resove(item)
            }
        })
    })
}
Copy the code