The main purpose of this series is to help a lot of students who don’t know much about writing code, think they know it, and when they really encounter a deeper problem, they still have to rely on search to solve the problem. The main purpose of this series is to move away from search and write code that feels confident. No matter how the problem changes we have a 360 degree solution.

Plans for the series can be seen at Github /core-rewrite. Welcome to exchange more. At the same time, byte e-commerce hangzhou team recruitment, come to join! For more information, please scan the public account at the bottom

Promise core implementation

This is the first installment in the code implementation series – the Promise core implementation

More questions are welcome to discuss in the Promise core implementation forum

The premise to prepare

This time we are to realize the Promise, to help us have a deeper understanding of the Promise.

Before you do that, you need to understand what Promise is and how Promise is used. If you don’t know Promise, you can click here. Okay

Before writing thinking

Before implementing a Promise, we need to think about what it involves to implement a Promise, for example

  • Promise represents an asynchronous result
  • Promise has three states, and the states can only start frompendingtofulfilledorpendingtorejected
  • Promise can be called chained

So with those three points in mind, we can start implementing them, so let’s go through them step by step

Code implementation

Constructor implementation

First, let’s define a function, which we’ll call CorePromise so as not to conflict with the original Promise

function CorePromise(handler) {
    // write code here
 }
Copy the code

First we define the state of the Promise. The Promsie’s initial state is pending.

We also know that the Promise constructor takes an argument, which is a function that accepts resolve and reject, so that we can call it, and we implement this logic as follows

 function CorePromise(handler) {
    // Three kinds of state pending, rejected, depressing
    this.status = 'pending';

    function resolve() {}function reject() {

    }

    handler(resolve, reject);
 }
Copy the code

What happens when resolve or reject is called? The promise state changes, and we implement resolve and reject functions that only execute in pending state

Meanwhile, since it is the Promise that needs to be executed asynchronously, we use setTimeout to wrap it here.

After the call we record the result of the success and the cause of the failure.

 function CorePromise(handler) {
    // Three kinds of state pending, rejected, depressing
    this.status = 'pending';
    this.value = null;
    this.err = null;

   const resolve = (value) = > {
        if (this.status === 'pending') {
            setTimeout(() = > {
                this.status = 'fulfilled';
                this.value = value; }}})const reject = (err) = > {
        if (this.status === 'pending') {
            setTimeout(() = > {
                this.status = 'rejected';
                this.err = err;
            })
        }
    }

    handler(resolve, reject);
 }
Copy the code

Calling resolve or Reject In addition to the state change, we continue to execute the callback defined in calling THEN and pass the result or failure reason to that callback.

Let’s define two arrays to mount callback functions and iterate over them as they are executed.

We also error handler(resolve, reject), and reject the Promise when it returns an error.

function CorePromise(handler) {
    // Three kinds of state pending, rejected, depressing
    this.status = 'pending';
    this.value = null;
    this.err = null;
    this.resolveCbs = [];
    this.rejectCbs = [];

    const resolve = (value) = > {
        if (this.status === 'pending') {
            setTimeout(() = > {
                this.status = 'fulfilled';
                this.value = value;
                this.resolveCbs.forEach(cb= > cb(this.value)); }}})const reject = (err) = > {
        if (this.status === 'pending') {
            setTimeout(() = > {
                this.status = 'rejected';
                this.err = err;
                this.rejectCbs.forEach(cb= > cb(this.err)); }}})try {
        handler(resolve, reject);
    } catch(err) {
        reject(err)
    }
 }
Copy the code

That’s it for the constructor implementation. Let’s look at the implementation of THEN.

Then achieve

The primary goal of then is to mount callback functions that can be executed when the Promise state changes.

And because THEN supports chained calls, then itself also needs to return a new Promise.

Then takes two arguments, the first to handle the result of a successful Promise, and the second to handle the result of a failed Promise. When these two functions do not exist, we give them default values and pass the result or error directly back.

CorePromise.prototype.then = function(resolveCb, rejectCb) {
    let promise = null;
    resolveCb = typeof resolveCb === 'function' ? resolveCb : (value) = > value;
    rejectCb = typeof rejectCb === 'function' ? rejectCb : (err) = > { throw err };

    promise = new CorePromise((resolve, reject) = >{});return promise
}
Copy the code

Now comes the most important part, implementing the THEN logic. There are two points here

  • ifPromiseIt’s already in the final state, so I’m going to go aheadthenThe returnedPromseIt also goes to its final state.
  • If the currentPromiseIs still inpendingState, so we save the handler inPromiseTo be called after entering the final state.

Important: Note the distinction between the two Promise instances involved: the current Promise and the new Promise returned in then. The new Promise returned changes after the Promise state changes

So that’s pretty much the core implementation, but we’re going to have to deal with a little extra special case for all the scenarios

  • The callback function for then results in anotherPromiseExamples (this is the same as the two mentioned earlierPromiseAre different, belong to the third, there is a little round, need to think clearly)
  • The result of the callback function of thenPromiseReturned with the current THENPromiseInstances that are the same cause a loop call that needs to throw an error
CorePromise.prototype.then = function (onFulfilled, onRejected) {CorePromise.prototype.then = function (onFulfilled, onRejected) {
    let promise = null;
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) = > value;
    onRejected = typeof onRejected === 'function' ? onRejected : (err) = > { throw err };

    const handlePromise = (modifier, resolve, reject) = > value= > {
        try {
            const x = modifier(value);

            if (x === promise) {
                // The same object will be called in a loop, directly reporting an error
                reject(new TypeError('Chaining cycle detected for promise! '))
                return;
            }

            / / x is thenable
            if (x && typeof x.then === 'function') {
                x.then(resolve, reject);
            } else {
                // x is not thenableresolve(x); }}catch(err) {
            reject(err);
        }
    }

    promise = new CorePromise((resolve, reject) = > {
        if (this.status === 'fulfilled') {
            handlePromise(onFulfilled, resolve, reject)(this.value);
        } else if (this.status === 'rejected') {
            handlePromise(onRejected, resolve, reject)(this.err);
        } else {
            this.resolveCbs.push(handlePromise(onFulfilled, resolve, reject));
            this.rejectCbs.push(handlePromise(onRejected, resolve, reject)); }});return promise
}
Copy the code

After handling the special case, we’ll finally implement the first catch function, which catches the error in the Promise call chain and is simple to implement, just a syntactic sugar for then

CorePromise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected)
}
Copy the code

Promise is often implemented by functions

Resolve, Promise. Reject, Promise. All, and Promise. Race. The first two are just grammatical sugar

Promise.resolve

Promise.resolve implementation, which returns a Promise that immediately enters the resolve state

CorePromise.resolve = (value) = > {
    return new CorePromise((resolve) = > {
        resolve(value)
    })
}
Copy the code

Promise.reject

Promise. Reject implementation, which returns a Promise that immediately goes into reject

CorePromise.reject = (error) = > {
    return new CorePromise((resolve, reject) = > {
        reject(error)
    })
}
Copy the code

Promise.all

Promise.all is not difficult to implement, there are two points to note

  1. When aPromiseEnter therejectState, the whole resultPromiseEnter therejectstate
  2. When allPromiseNote the order in which the result array is returned, because the code does not directly return the resultpushEnter arrays instead usingresult[i] = resThis way and cooperatetotalCount the reasons to achieve
CorePromise.all = (promises) = > {
    const result = []
    const length = promises.length;
    let total = 0;
    return new CorePromise((resolve, reject) = > {
        for (let i = 0; i < length; i++) {
            const promise = promises[i];
            promise.then(res= > {
                result[i] = res;
                total++;

                if(total === length) { resolve(result); }},(err) = >{ reject(err); })}})}Copy the code

Promise.race

Promise.race implementation, which Promise is executed first directly ignores the other promises

CorePromise.race = (promises) = > {
    let isHandled = false
    return new CorePromise((resolve, reject) = > {
        for (let i = 0; i < promises.length; i++) {
            promises[i].then((res) = > {
                if(! isHandled) { isHandled =true;
                    resolve(res)
                }
            }, reject)
        }
    })
}
Copy the code

Read well

Recruitment & front-end communication, or directly add VX: Stellarecho