Writing promises is a must for almost every major factory, so I took some time to research them, and here’s the thought process for making promises come true. If you don’t mind, please read:

The introduction of promise

A Promise is simply a container that holds the result of an event (usually an asynchronous operation) that will end in the future. Syntactically, a Promise is an object from which to get messages for asynchronous operations. Here’s an example of a promise:

const promise = new Promise(function(resolve, reject) {
  // ... some code

  ifResolve (value); }else{ reject(error); }});Copy the code

It can be seen that the constructor of a promise contains two methods: resolve and reject. Meanwhile, according to the PROMISE + specification, a promise contains 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. Then we can implement resolve, Reject, and THEN according to these three different states, and a simple promise prototype emerges. Here’s how to do it:

Promise constructor:

We can start by writing a constructor as follows:

/** * create three variable records to represent the state * that saves this, The value variable is used to hold values passed in resolve or reject. The resolvedCallbacks and rejectedCallbacks are used to hold valuesthenBecause the state may still be waiting when the Promise is executed, it should be calledthenIs saved for state changes using */ const PENDING ='pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

functionmyPromise(fn){ const that = this; that.value = null; that.status = PENDING; // The default state is that. that.rejectedCallbacks = [];function resolve(value){
    if(that.status === PENDING ){
      }
  }
 function reject(value){
    if(tha.status === PENDING){}} // Execute the callback function try{fn(resolve, reject)}catch (e) {reject(e); }}Copy the code

So what do you do in Resolve? This will be a pity state. At the same time, save the value passed in, so that the following THEN can be used. Finally, the method in the callback can be implemented to implement the callback. Reject, reject, reject, reject, reject, reject

/** * create three variable records to represent the state * that saves this, The value variable is used to hold values passed in resolve or reject. The resolvedCallbacks and rejectedCallbacks are used to hold valuesthenBecause the state may still be waiting when the Promise is executed, it should be calledthenIs saved for state changes using */ const PENDING ='pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

functionmyPromise(fn){ const that = this; that.value = null; that.status = PENDING; // The default state is that. that.rejectedCallbacks = [];function resolve(value){
    if(that.status === PENDING ){ that.status = FULFILLED; that.value = value; / / execute callback methods that. FulfilledCallbacks. ForEach (myFn = > myFn (value) that.)}}function reject(value){
    if(that.status === PENDING ){ that.status = REJECTED; that.value = value; / / execute callback methods that. RejectedCallbacks. ForEach (myFn = > myFn (value) that.)}} / / implement the callback function try {fn (resolve, reject) }catch (e) { reject(e); }}Copy the code

So the promise constructor is done, and the implementation of then is done. Is there a little excitement ~~~

Implementation of then in a promise

Considering that all instances use the THEN method, the THEN must be placed on the promise prototype chain. What do I do when the state is PENDING? Instead of performing a callback, place the callback methods on separate stacks and wait to be invoked. When the state is depressing or REJECTED, the response method can be implemented. So:

   myPromise.prototype.then = function (onFulfilled, onRejected){
    letself = this; // add a callback function to the stackif(self.status === PENDING){ self.fulfilledCallbacks.push(()=>{ onFulfilled(self.value); }); self.rejectedCallbacks.push(()=>{ onRejected(self.value); })}if(self.status === FULFILLED){
        onFulfilled(self.value);
    }

    if(self.status === REJECTED){
        onRejected(self.value)
    }
}
Copy the code

So a simple promise is implemented as follows:

/** * create three variable records to represent the state * that saves this, The value variable is used to hold values passed in resolve or reject. The resolvedCallbacks and rejectedCallbacks are used to hold valuesthenBecause the state may still be waiting when the Promise is executed, it should be calledthenIs saved for state changes using */ const PENDING ='pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

functionmyPromise(fn){ const that = this; that.value = null; that.status = PENDING; // The default state is that. that.rejectedCallbacks = [];function resolve(value){
    if(that.status === PENDING ){ that.status = FULFILLED; that.value = value; / / execute callback methods that. FulfilledCallbacks. ForEach (myFn = > myFn (value) that.)}}function reject(value){
    if(that.status === PENDING ){ that.status = REJECTED; that.value = value; / / execute callback methods that. RejectedCallbacks. ForEach (myFn = > myFn (value) that.)}} / / implement the callback function try {fn (resolve, reject) }catch (e) { reject(e); } } myPromise.prototype.then =function (onFulfilled, onRejected){
    letself = this; // add a callback function to the stackif(self.status === PENDING){ self.fulfilledCallbacks.push(()=>{ onFulfilled(self.value); }); self.rejectedCallbacks.push(()=>{ onRejected(self.value); })}if(self.status === FULFILLED){
        onFulfilled(self.value);
    }

    if(self.status === REJECTED){
        onRejected(self.value)
    }
}

let p = new thePromise((resolve, reject)=>{
    console.log('hello');
    resolve(5);
});
p.then((res)=>{
    console.log(res);
})
p.then(()=>{
    console.log('jj');
})
Copy the code

The results are as follows:

However, have you found that then does not return a promise, which is not in accordance with the specification, so we can partially optimize the promise.

Transform promise

A promise can be passed to a THEN, as follows:

  • For the resolve function, you first need to determine whether the value passed in is of type Promise
  • To ensure that the functions are executed in order, you need to wrap the two function body code with setTimeout
 function resolve(value) {
        if(value instanceof myPromise){
            return value.then(resolve, reject);
        }
        setTimeout(()=>{ that.status = FULFILLED; that.value = value; / / will perform a callback method that fulfilledCallbacks. ForEach (myFn = > myFn (value) that.)}, 0)}function reject(value) {
        setTimeout(()=>{ that.status = REJECTED; that.value = value; / / will perform a callback method that rejectedCallbacks. ForEach (myFn = > myFn (value) that.)}, 0). }Copy the code

Then we need to add a variable promise2, because every then function returns a new Promise object that holds the new return object.

myPromise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    let promise2 = null;
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
    onRejected = typeof onRejected === 'function'? OnRejected: r => {throw r} // Wait, then add the callback function to the stackif(self.status === PENDING){
        return (promise2 = new myPromise((resolve, reject)=>{
            self.fulfilledCallbacks.push(()=>{
                try {
                    let x = onFulfilled(self.value);
                    resolutionProduce(promise2, x, resolve, reject);
                }catch (e) {
                    reject(e)
                }
            });
            self.rejectedCallbacks.push(()=>{
                try {
                    let x = onRejected(self.value);
                    resolutionProduce(promise2, x, resolve, reject);
                }catch (e) {
                    reject(e);
                }
                onRejected(self.value);
            });
        }));
    }
    if(self.status === FULFILLED){
        return(promise2 = new myPromise((resolve, reject)=>{
            setTimeout(()=>{
                try {
                    letx = onFulfilled(self.value); resolutionProduce(promise2, x, resolve, reject); }catch (e) { reject(e); }}}, 0))); }if(self.status === REJECTED){
        return (promise2 = new myPromise((resolve, reject)=>{
            setTimeout(()=>{
                try {
                    letx = onRejected(self.value); resolutionProduce(promise2, x, resolve, reject) }catch (e) { reject(e); }}}, 0))); }}Copy the code

This is basically the same as before, except that the return value is changed to PROMISE, and the exception is captured. When the status is FULFILLED with pity or REJECTED, the asynchronous setTimeout package should be added. The core resolutionProduce function is now complete:

  • The specification has to ensure that current X is not the same as promisE2, otherwise meaningless identical operations will be performed, resulting in circular references. Such as:
let p = new myPromise((resolve, reject) => {
  resolve(1)
})
let p1 = p.then(value => {
  return p1
})
Copy the code
  • And then we need to determine the type of x
if (x instanceof MyPromise) {
    x.then(function(value) {
        resolutionProcedure(promise2, value, resolve, reject)
    }, reject)
}
Copy the code

The code here is implemented in full compliance with the specification. If x is a Promise, you need to determine the following:

  1. If X is in wait state, the Promise needs to remain in wait state until x is executed or rejected
  2. If x is in any other state, the Promise is processed with the same value
function resolutionProduce(promise, x, resolve, reject){
 if(promise === x){
        return reject(new TypeError('Error'));
 }
 let called = false;
 if(x ! == null && (typeof x ==='object' || typeof x === 'function')){
        try {
            let then = x.then;
            if(typeof then= = ='function'){
                then.call(x, y=>{
                    if(called) return;
                    called = true;
                    resolutionProduce(promise2, y, resolve, reject )
                }, e =>{
                    if(e) return;
                    called = true; reject(e); })}else {
                resolve(x);
            }
        }catch (e) {
            if(called) return;
            called = true; reject(e); }}else{ resolve(x); }}Copy the code
  • First create a variable called to determine whether the function has been called
  • Then determine whether x is an object or a function, and if neither is, pass x to resolve
  • If x is an object or function, assign x. teng to then, then determine the type of the then, and pass x to resolve if it is not a function type
  • If then is a function type, x is called as the function’s scope this and two callback functions are passed as arguments, the first called resolvePromise and the second called rejectPromise. Both callback functions need to determine whether the function has already been executed, and then do the corresponding logic
  • If you throw something wrong, you pass the error into the Reject function

The above is a simple implementation of promise. Next time, we will implement catch, all, race and other methods when we have time. Please look forward to the future. Reference: juejin. Cn/post / 684490…