** Let’s start with the humblest promise

let p=new Promise(function(resolev,reject){
    console.log('execution')})Copy the code
The browser will type “Execute”…. From this we can infer that the promise structure should be:
Define a promise, passing a parameter that is a function that contains two methods, resolve and reject, that are immediately executed within the promise
function Promise(executor){
    function resolve(){ }
    function reject(){} // This executor can be thought of as a self-executing function.function(resolev,reject){
            console.log('execution') 
        })(resolve,reject)
    */
    executor(resolve,reject)
}
Copy the code

Promise has three states: pending, Resolved and Rejected. Promise cannot change its state until it is resolved
Pending ——————> Resolved ——————> successful…
Pending ——————>rejected waiting ——————> failed… ರ _ ರ
/ / the original promiselet p=new Promise(function(resolev,reject){// resolve(100) rejecte(200)}function(data){
      console.log(data,'resolved')},function(error){
      console.log(error,'rejecte')})Copy the code
In native promises, only the first one of resolve and Reject is called. And a call to resolve executes the first callback in THEN, and a call to Reject executes the second callback in THEN
To do this, we need to do it in our own promise function
  • Define status, which stores the current status
  • Define value to store the value passed in the successful call to resolve
  • Define reason to store error messages for reject failed calls
  • Define a THEN function and take two arguments of type function, the first a successful callback and the second a failed callback
  • As you can see from the then method in a promise, only one of these methods will be executed

function Promise(executor) {
    let self = this
    this.status = 'pending'This. value = undefined // Successful value this.reason = undefined // Failed reasonfunction resolve(value) {
      if (self.status == 'pending') {
        self.status = 'resolved'
        self.value = value
      }
    }
    function reject(error) {
      if (self.status == 'pending') {
        self.status = 'rejected'
        self.reason = error
      }
    }
    executor(resolve, reject)
  }
  Promise.prototype.then = function// Only one method will be implemented according to the statusif (this.status == 'resolved') {
      infulfilled(this.value)
    }
    if (this.status == 'rejected') {
      inrejected(this.reason)
    }
  }
Copy the code

Now that promise is starting to look like something… It’s far from perfect, though, because the promise’s highlight is the problem of asynchronous callback hell, but the handwritten promise only supports synchronous code, as shown in the following example:

// Use your own promise~ not native ~~~!let p = new Promise(function (resolve, reject) {
  setTimeout(() => {resolve(100)},1000)})function (data) {
  console.log(data, 'resolve')},function (error) {
  console.log(error, 'reject')})Copy the code

As you can see, the then method is not executed. Why

Actually it has been implemented.. Just because the state is still pending… Neither of the two if conditions is satisfied. So I’m going to skip it,
Now, why is that
  • When wenew Promise(function (resolve, reject) { setTimeout(() => { resolve(100) },1000) })whenexecutor()It will be executed immediately, but don’t forget there’s asetTimeout, soexecutor()The action inside takes 1000ms
  • However, because the synchronous function is faster than the asynchronous function, so in this casethenThe method starts executing, howeverPromiseThe inside of thestatusIs stillpendingWait state,thenMethod scanned the interior… Found that status does not satisfy any of the conditions,so.. it’s ending… The end of the
  • After the 1000 ms,executor()In theresolveIt’s going to execute. It’s going tostatusChanged toresolvedAnd assign the value tovalue. But there was no follow-up,so.. it’s ending…

Make your promise support asynchronous calls

  • Define two arrays onResovedCallbacks and onRejectedCallbacks in promise to hold successful and failed callbacks
  • Judge in thenstatusIf it ispendingWait state, then store successful and failed callbacks in the above two arrays respectively
  • Execute the onResovedCallbacks and onRjectedCallbacks in the Promise’s resolve and Reject methods
 function Promise(executor) {
    let self = this
    this.status = 'pending'This. value = undefined // Store successful value this.reason = undefined // Store failed reason this.onResolvedCallbacks = []// Store successful callback This.onrejectedcallbacks = []// Store failed callbacksfunction resolve(value) {
      if (self.status == 'pending') {
        self.status = 'resolved'Self. Value = the value / / traverse, execute the infulfilled () method of the self. OnResolvedCallbacks. ForEach (fn = > fn ()); }}function reject(error) {
      if (self.status == 'pending') {
        self.status = 'rejected'Self. reason = errorinThe Rejected () method of the self. OnRejectedCallbacks. ForEach (fn = > fn ())}} executor (resolve, reject)} Promise. Prototype. Then =function (infulfilled, inrejected) {
    let self = this
    if (this.status == 'resolved') {
      infulfilled(this.value)
    }
    if (this.status == 'rejected') {
      inrejected(this.reason)
    }
    if (this.status == 'pending') {/ / at this moment is not resolved is not rejected state this. OnResolvedCallbacks. Push (function () {
        infulfilled(self.value)
      })
      this.onRejectedCallbacks.push(function () {
        inrejected(self.reason)
      })
    }
  }
   let p = new Promise(function (resolve, reject) {
    setTimeout(() => {
      resolve(100)
    }, 2000)
  })
  p.then(function (data) {
    console.log(data, 'resolve')},function (error) {
    console.log(error, 'reject')})Copy the code

Now you can try setTimeout. Well, asynchronous calls are already supported

But there is another case, which is whennew Promise()Throws an exception, for example
let p = new Promise(function (resolve, reject) {
    throw new Error('wrong')
  })
  p.then(function (data) {
    console.log(data, 'resolve')},function (error) {
    console.log(error, 'reject')})Copy the code

In this case, the successful and failed callbacks to the THEN method will not be called. Instead, an error will be reported, because status is still pending, so we need to catch the exception inside the Promise function:

 function Promise(executor) {
    let self = this
    this.status = 'pending'This. value = undefined // Store successful value this.reason = undefined // Store failed reason this.onResolvedCallbacks = []// Store successful callback This.onrejectedcallbacks = []// Store failed callbacksfunction resolve(value) {
      if (self.status == 'pending') {
        self.status = 'resolved'self.value = value self.onResolvedCallbacks.forEach(fn => fn()); }}function reject(error) {
      if (self.status == 'pending') {
        self.status = 'rejected'The self. "reason = error self. OnRejectedCallbacks. ForEach (fn = > fn ())}} / / catch exceptions to direct failed... try { executor(resolve, reject) } catch (error) { reject(error) } } Promise.prototype.then =function (infulfilled, inrejected) {
    let self = this
    if (this.status == 'resolved') {
      infulfilled(this.value)
    }
    if (this.status == 'rejected') {
      inrejected(this.reason)
    }
    if (this.status == 'pending') {
      this.onResolvedCallbacks.push(function () {
        infulfilled(self.value)
      })
      this.onRejectedCallbacks.push(function () {
        inrejected(self.reason)
      })
    }
  }
  let p = new Promise(function (resolve, reject) {
    throw new Error('wrong')
  })
  p.then(function (data) {
    console.log(data, 'resolve')},function (error) {
    console.log(error, 'reject111')})Copy the code
You can now successfully catch the exception

Now the handwritten promise from the original promise and more like a little bit, but the biggest feature of the original ~ chain call, we have not implemented,

Analyze the implementation method of ~ chain call

To ensure that a chained call returns a new one regardless of the success/failure/wait statePromiseTo continue calling the THEN method

So promise2 equals newPromiseTo resolve, the executor will be executed immediately when a new Promise is made

  function Promise(executor) {
    let self = this
    this.status = 'pending'This. value = undefined // Store successful value this.reason = undefined // Store failed reason this.onResolvedCallbacks = []// Store successful callback This.onrejectedcallbacks = []// Store failed callbacksfunction resolve(value) {
      if (self.status == 'pending') {
        self.status = 'resolved'self.value = value self.onResolvedCallbacks.forEach(fn => fn()); }}function reject(error) {
      if (self.status == 'pending') {
        self.status = 'rejected'
        self.reason = error
        self.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }
  Promise.prototype.then = function (infulfilled, inrejected) {
    let self = this
    let promise2
    if (this.status == 'resolved') {
      promise2 = new Promise(function (resolve, reject) {
        infulfilled(self.value)
      })
    }
    if (this.status == 'rejected') {
      promise2 = new Promise(function (resolve, reject) {
        inrejected(self.reason)
      })
    }
    if (this.status == 'pending') {
      promise2 = new Promise(function (resolve, reject) {
        self.onResolvedCallbacks.push(function () {
          infulfilled(self.value)
        })
        self.onRejectedCallbacks.push(function () {
          inrejected(self.reason)
        })
      })
    }
    return promise2
  }
Copy the code

This code looks simple enough to return a new promise

Secondly, the native promise states that a successful callback or a failed callback will enter the successful callback of the next THEN if the result is returned, and the failed callback of the next THEN if there is an exception.

Let’s take a look at what then does after it throws an exception

*** Take a look at the following code

// Both native promises and today's handwritten promises can do thislet p = new Promise(function (resolve, reject) {
    resolve(1000)
    //reject(2000)
  })
  let p2 = p.then(function (data) {
    throw new Error('wrong')},function (error) {
    throw new Error('Failed')
  })
  p2.then(function (data) {
    console.log(data, 'resolve');
    }, function (error) {
    console.log(error, 'rejected');
  })
Copy the code

As you can see, if resolve() or reject() is used in the Promise instance, an exception is thrown in then and the failed callback for the next THEN is entered

Step by step, break down the execution flow of the catch exception, starting with the previous figure

Think about the ideas in the picture
  • The first step
// Resolve was called first, so the promise status is Resolvedlet p = new Promise(function (resolve, reject) {
    resolve(1000)
  })
Copy the code
  • Step 2 CallthenMethod,thenMethod based on the current stateresolvedEnter the firstifIn the condition
  • 2.1 A new Promise instance is initialized
  • 2.2 New Promiese instances will be executed immediatelyinfulfilledThis is called, but now indepressing throws an exception and is renewedpromise try catchCatch, reject method is called, newpromisestatestatusBe changed torejectFailed state,reasonIs equal to becatchError message received
let p2 = p.then(function (data) {
    throw new Error('wrong')},function (error) {
    throw new Error('Failed')})Copy the code
  • Step three, now P2 is the new promise, and the state isreject reasonIs the error message that was just caught and enters the second if condition when it calls the then method because the current state isrejectWell,
  • 3.1 Newly initialize a Promise instance
  • 3.2.. The Promise is executed immediately, the inRejected () method is called, then the second callback is executed. Print error message
 p2.then(
    function (data) {
      console.log(data, 'resolve');
    },
    function (error) {
      console.log(error, 'rejected');
    })
Copy the code
Finally finished analyzing exception handling…
Now let’s analyze the return value of success or failure,

Native Primse executes the then method and may sometimes return a result, either a regular value or a promise, as the value of the next promise or as the reason for the failure

let p = new Promise(function (resolve, reject) {
    resolve(1000)
  })
  let p2 = p.then(function(data) {// returns a normal value //return 'test'// Return a promisereturn new Promise(function (resolve, reject) {
      //resolve('success')
      reject('failure')})},function (error) {
    throw new Error('failed')
  })
   p2.then(
    function (data) {
      console.log(data, 'resolveq');
    },
    function (error) {
      console.log(error, 'rejected ');
    })
Copy the code

To achieve this effect, we need to refine the then method of handwritten promises by writing a function resolvePromise that handles the result returned after the then method is executed

  Promise.prototype.then = function (infulfilled, inrejected) {
    let self = this
    let promise2
    if (this.status == 'resolved') {
      promise2 = new Promise(function(resolve, reject) {//x can be a promise, or it can be a normal valueletThis is a big pity (self.value) // The resolve and reject values are promise2, and x is a big pity. The return value of X may be a promise or a normal value, so it will be processed uniformly resolvePromise(promise2, x, resolve, reject) }) }if (this.status == 'rejected') {
      promise2 = new Promise(function(resolve, reject) {//x can be a promise, or it can be a normal valueletX = inrejected(self.reason) // The return value of X is promise2, which may be a promise or a common value, so it will be processed uniformly resolvePromise(promise2, x, resolve, reject) }) }if (this.status == 'pending') {
      promise2 = new Promise(function (resolve, reject) {
        self.onResolvedCallbacks.push(function() {//x can be a promise, or it can be a normal valueletThis is a big pity (self.value) // The resolve and reject values are promise2, and x is a big pity. The return value of X may be a promise or a normal value, so it will be processed uniformly resolvePromise(promise2, x, resolve, reject) }) self.onRejectedCallbacks.push(function() {//x can be a promise, or it can be a normal valueletX = inrejected(self.reason) // The return value of X is promise2, which may be a promise or a common value, so it will be processed uniformly resolvePromise(promise2, x, resolve, reject) }) }) }return promise2
  }
Copy the code

Internal implementation of resolvePromise

  • 1. First, in the native Promise, the result returned by the then method may be itself, which never succeeds or fails (circular reference) and throws an exception of the wrong type

    So inside the resolvePromise you need to determine whether the result returned is the same as the Promise

  • 2. Determine if x is the samepromiseIf it ispromiseIf x is an object and the then method of x is a function, then it is a promise
  • 3 if x is notpromiseThat should be normal value straightresolve
  • 4. If x is apromiseLet’s see if he has onethenmethods
function resolvePromise(p2, x, resolve, reject) {
    if(p2 === x && x ! = undefined) { reject(new TypeError('Type error'} // It could be a promise, see if there is one in the objectthenMethod, if there is a ~ then it's a promiseif(x ! == null && (typeof x ==='object' || typeof x === 'function') {try {// To prevent {then:11} In this case, judgment is requiredthenIs it a functionlet then = x.then
        if (typeof then= = ='function') {
          then.call(x, functionResolvePromise (p2, y, resolve, reject) resolvePromise(p2, y, resolve, reject)}function (err) {
            reject(err)
          })
        } else{/ / ifthennotfunctionThat could be an object or a constant resolve(x)}} Catch (e) {reject(e)}}elseResolve (x)}}Copy the code

Perfect it… Should our code be able to pass nothing in THEN, implementation worth penetrating

let p = new Promise(function (resolve, reject) {
   resolve(1000)
 })
 p.then().then().then(function (data) {
   console.log(data, 'resolve');
 }, function (error) {
   console.log(error, 'reject');
})
Copy the code

Therefore, in the THEN method, there is a fault tolerance process for the parameter “Infulfilled” and “Rejected”. For the parameter “infulfilled”, the original parameter will be used. If there is no parameter, the default function will be given

Promise.prototype.then = function (infulfilled, inrejected) {
   let self = this
   letPromise2 // If indepressing is notfunctionThis is a default function that returns val indepressing = typeof indepressing ==='function' ? infulfilled : function (val) {
     returnInt rejected = typeof inRejected ==='function' ? inrejected : function (err) {
     throw err
   }
   if (this.status == 'resolved') {
     promise2 = new Promise(function(resolve, reject) {//x can be a promise, or it can be a normal valuelet x = infulfilled(self.value)
       resolvePromise(promise2, x, resolve, reject)
     })
   }
   if (this.status == 'rejected') {
     promise2 = new Promise(function(resolve, reject) {//x can be a promise, or it can be a normal valuelet x = inrejected(self.reason)
       resolvePromise(promise2, x, resolve, reject)
     })
   }
   if (this.status == 'pending') {
     promise2 = new Promise(function (resolve, reject) {
       self.onResolvedCallbacks.push(function() {//x can be a promise, or it can be a normal valuelet x = infulfilled(self.value)
         resolvePromise(promise2, x, resolve, reject)
       })
       self.onRejectedCallbacks.push(function() {//x can be a promise, or it can be a normal valuelet x = inrejected(self.reason)
         resolvePromise(promise2, x, resolve, reject)
       })
     })
   }
   return promise2
 }
Copy the code

Finally, the ~promise specification requires that all the Infulfilled and the Rejected should be performed asynchronously, so here is the whole code for all the Infulfilled and the Rejected with setTimeout.

  function Promise(executor) {
   let self = this
   this.status = 'pending'This. value = undefined // Store successful value this.reason = undefined // Store failed reason this.onResolvedCallbacks = []// Store successful callback This.onrejectedcallbacks = []// Store failed callbacksfunction resolve(value) {
     if (self.status == 'pending') {
       self.status = 'resolved'self.value = value self.onResolvedCallbacks.forEach(fn => fn()); }}function reject(error) {
     if (self.status == 'pending') {
       self.status = 'rejected'
       self.reason = error
       self.onRejectedCallbacks.forEach(fn => fn())
     }
   }
   try {
     executor(resolve, reject)
   } catch (error) {
     reject(error)
   }
 }
 Promise.prototype.then = function (infulfilled, inrejected) {
   let self = this
   let promise2
   infulfilled = typeof infulfilled === 'function' ? infulfilled : function (val) {
     return val
   }
   inrejected = typeof inrejected === 'function' ? inrejected : function (err) {
     throw err
   }
   if (this.status == 'resolved') {
     promise2 = new Promise(function(resolve, reject) {//x can be a promise, or it can be a normal valuesetTimeout(function () {
         try {
           letx = infulfilled(self.value) resolvePromise(promise2, x, resolve, reject) } catch (err) { reject(err) } }); })}if (this.status == 'rejected') {

     promise2 = new Promise(function(resolve, reject) {//x can be a promise, or it can be a normal valuesetTimeout(function () {
         try {
           letx = inrejected(self.reason) resolvePromise(promise2, x, resolve, reject) } catch (err) { reject(err) } }); })}if (this.status == 'pending') {
     promise2 = new Promise(function (resolve, reject) {
       self.onResolvedCallbacks.push(function() {//x can be a promise, or it can be a normal valuesetTimeout(function () {
           try {
             let x = infulfilled(self.value)
             resolvePromise(promise2, x, resolve, reject)
           } catch (err) {
             reject(err)
           }
         });
       })
       self.onRejectedCallbacks.push(function() {//x can be a promise, or it can be a normal valuesetTimeout(function () {
           try {
             letx = inrejected(self.reason) resolvePromise(promise2, x, resolve, reject) } catch (err) { reject(err) } }); })})}return promise2
 }
 function resolvePromise(p2, x, resolve, reject) {
   if(p2 === x && x ! = undefined) { reject(new TypeError('Type error'} // It could be a promise, see if there is one in the objectthenMethod, if there is a ~ then it's a promiseif(x ! == null && (typeof x ==='object' || typeof x === 'function') {try {// To prevent {then:11} In this case, judgment is requiredthenIs it a functionlet then = x.then
       if (typeof then= = ='function') {
         then.call(x, functionResolvePromise (p2, y, resolve, reject) resolvePromise(p2, y, resolve, reject)}function (err) {
           reject(err)
         })
       } else{/ / ifthennotfunctionThat could be an object or a constant resolve(x)}} Catch (e) {reject(e)}}elseResolve (x)}}Copy the code

Ok ~promise finished. Suggestions are welcome.