This is a simple version of the non-standard Promise implementation, which is more important, understand the content of the pre-knowledge point, also understand the basic principle of Promise.

Front knowledge

The core of this section is: Use function names as variables.

Once you understand these three things, you can make simple promises come true.


  1. A variable of type Function can be called like a Function.

Take a chestnut

let myFn = function () {};
Copy the code

You can just call it

myFn(); 
Copy the code

The effect is the same as the following code

function myFn() {
    // code here...
}

myFn();
Copy the code
  1. Function arrays, where each element of a function is a function, can be called by traversal.
let fnArray = [];
let fn1 = () => { console.log(1) };
let fn2 = () => { console.log(2) };
fnArray.push(fn1);
fnArray.push(fn2);
fnArray.forEach(cb => cb());
Copy the code

The result of running this code is

1
2
Copy the code
  1. Functions can be passed as arguments
functionShowYouFn (fn1, fn2) {// Fn1 (); fn2(); }let myFn1 = () => { console.log(3); };
let myFn2 = () => { console.log(4); };

showYouFn(myFn1, myFn2);
Copy the code

Recall: use function names as variables.

implementation

Here’s a Promise implementation that doesn’t look like a Promise

function MyPromise(fn) {
    function resolve(value) {
        console.log(value);
    }
    function reject(value) {
        console.log(value);
    }
    
    fn(resolve, reject);
}
Copy the code

Now you can use custom promises

new MyPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve('You see me! ');
    }, 2000);
});
Copy the code

Will output in 2 seconds

You saw me!Copy the code

The following code has the same effect as the above, written to emphasize again: functions are used as variables.

let promiseFn = (resolve, reject) => {
    setTimeout(()=>{
        resolve('You see me! ');
    }, 2000);
};
new MyPromise(promiseFn);
Copy the code

To explain the overall code:

The parameter fn in MyPromise is a custom function that needs to be passed in by the user (the function needs to take two arguments).

There are also two internal functions in MyPromise, resolve and Reject, where resolve represents the function that should be called if the user’s asynchronous task succeeds, and reject represents the function that should be called if the user’s asynchronous task fails.

How does the user invoke resolve and reject?

It is easy to pass the two functions as arguments to fn.

So MyPromise internally passes resolve and Reject to fn as arguments when calling fn.

The user then calls resolve or Reject within the custom function to notify MyPromise that the asynchronous task has finished.


I suggest copying the following code to the console

function MyPromise(fn) {
    function resolve(value) {
        console.log(value);
    }
    function reject(value) {
        console.log(value);
    }
    
    fn(resolve, reject);
}

new MyPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve('You see me! ');
    }, 2000);
});
Copy the code

One problem with the code above is that we don’t know where Promise’s asynchronous task is going, whether it succeeded or failed.

So add three states to indicate how far a Promise’s asynchronous task has progressed.

Pending, resolved, and Rejected indicate that the task is being executed, completed, and failed.

You can then determine the current Promise state by observing whether the user invokes resolve or reject.

Then there are three scenarios:

  1. When the user callsresolverejectThe previous state waspending
  2. The user callsresolveWhen, the state becomesresolved
  3. The user callsrejectWhen, the state becomesrejected

The following code changes define three constants to represent state and a variable state to store the current state.

And change state to Resolved when resolve is called.

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';

functionMyPromise(fn) { const that = this; // Pending that. State = pending;functionresolve(value) { console.log(value); That. State = RESOLVED; }functionreject(value) { console.log(value); // reject reject state that. State = REJECTED; } fn(resolve, reject); }Copy the code

If MyPromise’s resolve is called, then MyPromise knows that the task is completed, but the user doesn’t know.

So we need the callback to tell the user that, yes, it’s actually a callback.

This is where the then method comes in. The user passes in the callback function through then, and MyPromise calls the callback that the user passes in when resolve is successfully called.

The then method is used to assign the callback function passed in by the user to the variable in MyPromise.

So the then method looks like this: it takes two arguments, a callback on success and a callback on failure.

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';

functionMyPromise(fn) { const that = this; that.state = PENDING; // Two variables that store callback functions that. ResolvedCallback; that.rejectedCallback;functionresolve(value) { console.log(value); that.state = RESOLVED; ResolvedCallback (value); // Call the user's callback function and tell that.resolvedCallback && that.resolvedCallback(value); }functionreject(value) { console.log(value); that.state = REJECTED; RejectedCallback (value); // Call the user's callback function with the result that.rejectedCallback && that. } fn(resolve, reject); } MyPromise.prototype.then =function(onFulfilled, onRejected) {
    const that = this;
    that.resolvedCallback = onFulfilled;
    that.rejectedCallback = onRejected;
}
Copy the code

Yes, a simplified Promise is almost done, so let’s try executing the following in the browser (note that I removed the console statements in resolve and Reject)

(function () {
    const PENDING = 'pending';
    const RESOLVED = 'resolved';
    const REJECTED = 'rejected';
    functionMyPromise(fn) { const that = this; that.state = PENDING; // Two variables that store callback functions that. ResolvedCallback; that.rejectedCallback;functionresolve(value) { that.state = RESOLVED; ResolvedCallback (value); // Call the user's callback function and tell that.resolvedCallback && that.resolvedCallback(value); }functionreject(value) { that.state = REJECTED; RejectedCallback (value); // Call the user's callback function with the result that.rejectedCallback && that. } fn(resolve, reject); } MyPromise.prototype.then =function(onFulfilled, onRejected) {
        const that = this;
        that.resolvedCallback = onFulfilled;
        that.rejectedCallback = onRejected;
    }
    
    new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve('You saw me again.'); }, 2000); }).then(value => { console.log(value); }); }) ();Copy the code

The code above already looks similar to Promise in usage, but what if we call the then method multiple times?

Yes, only the callback function in the last then method can execute, which of course doesn’t satisfy our needs.

So, change the two callback functions into an array of functions (recall your prior knowledge) and iterate over the callback functions when the state changes.

The modified code is as follows:

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
functionMyPromise(fn) { const that = this; that.state = PENDING; / / note here into two arrays that resolvedCallbackArray = []; that.rejectedCallbackArray = [];functionresolve(value) { that.state = RESOLVED; / / traverse the user callback function, inform the results that the resolvedCallbackArray. ForEach (cbFn = > cbFn (value)); }functionreject(value) { that.state = REJECTED; / / traverse the user callback function, inform the results that the rejectedCallbackArray. ForEach (cbFn = > cbFn (value)); } fn(resolve, reject); } MyPromise.prototype.then =function(onFulfilled, onRejected) { const that = this; that.resolvedCallbackArray.push(onFulfilled); that.rejectedCallbackArray.push(onRejected); Mypromise.then ().then().then().then()...return that;
}
Copy the code

Now we can call the THEN method multiple times. Try running the following code on the console:

(function () {
    const PENDING = 'pending';
    const RESOLVED = 'resolved';
    const REJECTED = 'rejected';
    functionMyPromise(fn) { const that = this; that.state = PENDING; / / note here into two arrays that resolvedCallbackArray = []; that.rejectedCallbackArray = [];functionresolve(value) { that.state = RESOLVED; / / traverse the user callback function, inform the results that the resolvedCallbackArray. ForEach (cbFn = > cbFn (value)); }functionreject(value) { that.state = REJECTED; / / traverse the user callback function, inform the results that the rejectedCallbackArray. ForEach (cbFn = > cbFn (value)); } fn(resolve, reject); } MyPromise.prototype.then =function(onFulfilled, onRejected) {
        const that = this;
        that.resolvedCallbackArray.push(onFulfilled);
        that.rejectedCallbackArray.push(onRejected);
        return that;
    }
    
    new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve('You saw me again.');
        }, 2000)
    }).then(value => {
        console.log('First time', value);
    }).then(value => {
        console.log('The second time', value); }); }) ();Copy the code

This is already the implementation of a simplified version of Promise.

But we can go one step further and make MyPromise more robust.

For example, if an error occurs during the execution of a user-defined function, the program will be interrupted, so we add a try… catch… Statement, and actively execute the reject function to inform the user if an error occurs.

try {
    fn(resolve, reject);
} catch (e) {
    reject(e);
}
Copy the code

For example, then, if the parameter passed by the user is not a function? Or when the Promise state is rejected or resolved, then?

After reforming THEN, the code is as follows:

MyPromise.prototype.then = function(onFulfilled, onRejected) {
    if(typeof onRejected ! = ='function') {
        onRejected = v => v;
    }
    if(typeof onFulfilled ! = ='function') {
        onFulfilled = v => { throw r };
    }
    const that = this;
    if (that.state === PENDING) {
        that.resolvedCallbacks.push(onFulfilled)
        that.rejectedCallbacks.push(onRejected)
    }
    if (that.state === RESOLVED) {
        onFulfilled(that.value)
    }
    if (that.state === REJECTED) {
        onRejected(that.value)
    }
}
Copy the code

There are other ways you can adapt your own specifications once you understand the fundamentals. That concludes this article. Thank you for reading.