Promise Basic Edition (prototype)

Time:2020-10-25
function Promise(fn){
    //A successful callback is required
    var self = this
    var callback;
    //An instance method used to register asynchronous events
    self.then = function(done){
      callback = done;
    }
    //Resolve is executed before then, and the callback does not exist
    //So add a setTimeout to have the resolve function at the end of the callback queue
    //Why is 0 second? Why is it at the end of the callback queue?
    //(authoritative guide) if settimeout() is called with a timeout of 0 ms, the specified function will not be executed immediately
    //On the contrary, it will be put in the queue until all the event handlers in the waiting state have been executed,
    //Call it immediately.
    //The following way of writing indicates that resolve does not define problems involving closure scope chain
    // setTimeout(function () {
    //   function resolve(value){
    //      callback && callback(value);
    //   }
    // }, 0)
    
    //Ways to improve
    function resolve (value) {
      setTimeout(function () {
        callback && callback(value)
      }, 0)
    }
    fn(resolve);
  }

Call promise

//The instance promise callback function fn is executed successfully, and the resolve function is executed. At this time, add a
 //A callback function and pass the parameter of resolve.
 var promise = new Promise(function (resolve) {
    resolve(['3', 'aaa'])
 })
//Call the then function and execute the then callback. Assign the parameter done callback function of the then function to the callback,
//Execute then's callback function in the callback queue (the callback queue added by setTimeout before)
 promise.then(function (data) {
    console.log('data', data)
 })

However, in the above way, we can always execute only one callback queue in then, which is obviously not robust. We combine the publish message subscription mode of JS design pattern, and combine with the knowledge of the constructor return this to slightly modify the following:

function Promise(fn){
    //A successful callback is required
    var self = this
    self.deferreds  =[]; // the then function calls back to the queue storage container
    //An instance method used to register asynchronous events
    self.then = function(onFulfilled){
      self.deferreds.push(onFulfilled)
      console.log('self.deferreds', self.deferreds) 
      //Call the then callback twice, and the queue will push one by one
      Return self // chain call then
    }

    //Ways to improve
    function resolve (value) {
      setTimeout(function () {
        self.deferreds.forEach(function (deferred) {
           deferred && deferred(value)
        })
      }, 0)
    }
    fn(resolve);
}

Call the then function:

promise.then(function (data) {
    console.log('data', data)
}).then(function (resp) {
    console.log('resp', resp)
})

As we all know, the constructor promise has three mutually exclusive states: pending, fulfilled, rejected. There are only two possibilities to change the state of a promise object: from pending to fulfilled and from pending to rejected.As long as these two conditions occur, the state will solidify, will not change again, and will always maintain this result.

So: we improve the code as follows:

//Brief introduction
//Initialization setting status
self.status = 'pending'
//Brief introduction
// ...
//Brief introduction
//Set the state to
self.status = 'fulfilled'
//Brief introduction

Call execution, we can also get the data we want.
however
however
however
Just add the above two lines of code is not good, carefully understand the bold words, and then combined with our code.
When we call the then function, we push the callback function into the then callback queue, regardless of whether the status is pending
The function of callback queue is triggered by the resolve function. This goes against the saying:As long as these two conditions occur, the state will solidify, will not change again, and will always maintain this result.
When our state changes to fulfilled, we do not really change the state. Every time we re execute, we go again, then add a callback, and then resolve triggers the callback.
Therefore, the improved code of then function is as follows:

//When status = ='pending ', we push the callback function to the then callback queue.
//Otherwise, the callback function will be executed directly, and then's callback function will not be triggered by resolve.
if(self.status == 'pending') {
  self.deferreds.push(onFulfilled)
  return self
}
onFulfilled(value)
Return self // chain call then

Therefore, after adding value, the final code is as follows:
It basically implements a promise with pending and fulfilled states in chained call then
Later, reject () is added, along with the most difficult to understand serial project.

function Promise(fn){
    //A successful callback is required
    var self = this
    self.deferreds  =[]; // the then function calls back to the queue storage container
    self.status = 'pending'
    self.value = null
    //An instance method used to register asynchronous events
    self.then = function(onFulfilled){
      if(self.status == 'pending') {
        self.deferreds.push(onFulfilled)
        return self
      }
      onFulfilled(self.value)
      Return self // chain call then
    }

    //Ways to improve
    function resolve (newValue) {
      setTimeout(function () {
        self.value = newValue
        self.status = 'fulfilled'
        self.deferreds.forEach(function (deferred) {
           deferred && deferred(self.value)
        })
      }, 0)
    }
    fn(resolve);
 }

Appendix references

  • Meituan comments
  • Cn blog