preface

Promise is a key component of the front-end synchronous code execution mechanism. You may be using “await” es7 today, but understanding what promises are is still the focus of the front end. Let’s walk through the promise implementation step by step.

Synchronize the progress of the author’s blog source code series:

| | | blog theme sequence number link | | — – | — – | — — — — — — — — — — — – | | | 1 handwritten vue_mini source code parsing | juejin. Cn/post / 684790… | | 2 | handwritten react_mini source code parsing | juejin. Cn/post / 685457… | | 3 | handwritten webpack_mini source code parsing | juejin. Cn/post / 685457… | | | 4 handwritten jquery_mini source code parsing | juejin. Cn/post / 685457… | | | 5 handwritten vuex_mini source code parsing (that is, in this article) | juejin. Cn/post / 685529… | | | 6 handwritten vue_route source code parsing | juejin. Cn/post / 685956… 7 | | | handwritten diff algorithm source code parsing | juejin. Cn/post / 686881… | | | 8 written promise source code parsing | juejin. Cn/post / 686920… | | | 9 handwritten native js source code parsing (manual to realize common API) | is expected in September | | | handwritten react_redux, 10 fiberd source code parsing etc. | | September is expected to 11 | | handwritten koa2_mini | is expected in September, the front-end | priority

The concept of promise

In traditional projects, the front-end solves asynchronous problems with endless callback callbacks. In fact, small projects, or simpler logic, callback is most efficient. And then, as the program scales up, there’s what’s called callback hell. The community (or the ES6 specification) has introduced several asynchronous solutions to the problem where we can’t locate the entry, where there may be a problem with repeated calls, etc., and the problem is very difficult to track:

  • The callback function
  • Event listeners
  • Publish/subscribe
  • Promise object
  • await

Promise is designed to improve asynchronous programming in JS, giving you more control and composability over asynchronous operations than events and callbacks. The Promise schedule is added to the JS engine job queue for later execution. However, there is another job queue that tracks Promise completion and rejection handlers to ensure proper execution.

Of course, promises have some drawbacks:

  • 1) Promise cannot be cancelled. Once a Promise is created, it will be executed immediately and cannot be cancelled midway.
  • 2) If the callback function is not set, errors thrown internally by a Promise will not be reflected externally.
  • 3) When in the pending state, there is no way to know what stage of progress is currently in (just started or nearly completed).
  • 4) Compared with “await”, “then” is obviously more complicated in program code expression.

Let’s write the promise by hand and analyze the process.

The process of writing promise/A+ by hand

Promise/A+ does not regulate the race, all, and catch methods, which ES6 regulates itself. This paper focuses on the Promise itself, so it focuses on the construction of Promise itself and the implementation of then function.

The constructor

Let’s first examine what the constructor needs to handle.

  • 1.Promise has three states, namely pedding, Rejected and refulled. The default value is pedding. So first we’re going to record this state, _status.
  • 2. Each Promise has its own resolve and reject methods. We define asynFulliled, and call REJECT when asynRejected state changes to Rejected. Call resolve if the state transitions to refulLED
  • 3. After executing the current Promise, if the program works, the nextResolve and reject methods, nextResolve and nextReject, are performed.
  • 4. When the next Promise is called, the value of the current Promise is passed to the next Promise. We define _param to store it.

Based on our analysis, we can briefly list the following code:

class ZPromise{ constructor(){ var self = this; self._param = null; Self. _status = PEDDING; Resovle and reject self.nextresolve = null; resovle and reject self.nextresolve = null; // self.nextReject = null; // // record then method argument self.asynFulliled = null; self.asynRejected = null; self.then = function( onFulliled, onReject ){ // } } }Copy the code

Execute the handle method for the first time

Let’s look at how to create a promise or then method:

  new ZPromise(function (resolve, reject) {
      ...         
  }).then( function(result){
      ...
  })
Copy the code

After the construct executes for the first time, it first needs to execute the first… Things. We define it as handle. At this point, the first Promise might execute resolve() or reject(). So is resolve or reject something? Let’s implement _resolve and _reject.

  • First, resolve (or reject) changes the _status state. For example, after resolve, the state of promise changes to Rejected.
  • Second, the next argument passed in by a promise, even if the resolve value of the previous promise, is stored in _param.
  • If there is a next promise, the next promise method is called.

Modify the constructor:

class ZPromise{
    constructor(handle){
    	...
        handle(  _resolve,  _reject );
    }
}

function _resolve( val ){
    self._status = REFULLED;
    self._param = val;
 }
 
 function _reject( error ){
      _status = REJECTED;
      _param = error;
}
Copy the code

Then method

What do we do after we analyze the THEN method?

  • First, the then method is also a promise, and if it isn’t, we need to help it make a promise. That’s the constructor prototype pointing to the self object.

  • Then method, depending on the state of the object:

  • If the last promise was in failed state, this calls the failed callback (we call it doAsynRejected);

  • If the previous promise was in a successful state, this calls the successful callback (we call it: doAsynFulliled);

  • If it is still executing, we reassign the current object to nextResolve, nextReject, asynFulliled, asynRejected

    self.then = function( onFulliled, onReject ){ return new self.constructor( function( resovle, reject ){ if( self._status == REFULLED ){ doAsynFulliled( onFulliled, resovle, reject ); } else if ( self._status == REFULLED ){ doAsynRejected( onReject, resovle, reject ); } else {self.nextResolve = resovle; self.nextReject = reject; self.asynFulliled = onFulliled; self.asynRejected = onReject; }})}Copy the code

Before the initial initialization of Handle, the state at this time must be pedding. In order to facilitate the next promise(namely the promise in THEN) to know the corresponding resovle and reject, Then there are the corresponding promises, which we store separately (promises for successes and failures).

Assuming success, then we build the next Promise and call doAsynFulliled.

  • If the next promise is still a constructor, we create a new promise. If not, resolve returns the last value.

  • At this point, if the then of the next promise contains another promise, it needs to wait until the promise is fully implemented. Then we need to wait for the internal promise to be fulfilled.

    function doAsynFulliled( onFulliled, resolve, reject ){ if( typeof onFulliled == ‘function’ ){ let promise = onFulliled( self._param ); If (promise == undefined){// resolve(self._param); } else if ( promise.constructor == self.constructor ){ promise.then( function(param){ resolve( param ); }, function( param ){ reject( param ); }) } else { resolve( self._param ); } } else { resolve( self._param ); }}

In the same way:

function doAsynRejected( onRejected, resolve, reject ){ if( typeof onRejected == 'function' ){ let promise = onFulliled( self._param ); If (promise == undefined){reject(self._param); } else if ( promise.constructor == self.constructor ){ promise.then( function(param){ resolve( param ); }, function( param ){ reject( param ); }) } else { reject( self._param ); } } else { reject( self._param ); }}Copy the code

Let’s tidy it up:

var PEDDING = "pedding"; var REJECTED = "rejected"; var REFULLED = "refulled"; class MyPromise{ constructor(handle){ if( typeof("handle") == "function" ){ throw new Error("MyPromise handle is not a function"); } var self = this; self._param = null; Self. _status = PEDDING; Resovle and reject self.nextresolve = null; resovle and reject self.nextresolve = null; // self.nextReject = null; // // record then method argument self.asynFulliled = null; self.asynRejected = null; handle( _resolve, _reject ); self.then = function( onFulliled, onReject ){ return new self.constructor( function( resovle, reject ){ if( self._status == REFULLED ){ doAsynFulliled( onFulliled, resovle, reject ); } else if ( self._status == REFULLED ){ doAsynRejected( onReject, resovle, reject ); } else {self.nextResolve = resovle; self.nextReject = reject; self.asynFulliled = onFulliled; self.asynRejected = onReject; } }) } function _resolve( val ){ self._status = REFULLED; self._param = val; if( self.nextResolve ){ doAsynFulliled( self.asynFulliled, self.nextResolve, self.nextReject ); } } function _reject( error ){ _status = REJECTED; _param = error; if( nextReject ){ doAsynRejected( asynRejected, nextResolve, nextReject ); } } function doAsynFulliled( onFulliled, resolve, reject ){ if( typeof onFulliled == 'function' ){ let promise = onFulliled( self._param ); If (promise == undefined){// resolve(self._param); } else if ( promise.constructor == self.constructor ){ promise.then( function(param){ resolve( param ); }, function( param ){ reject( param ); }) } else { resolve( self._param ); } } else { resolve( self._param ); } } function doAsynRejected( onRejected, resolve, reject ){ if( typeof onRejected == 'function' ){ let promise = onFulliled( self._param ); If (promise == undefined){reject(self._param); } else if ( promise.constructor == self.constructor ){ promise.then( function(param){ resolve( param ); }, function( param ){ reject( param ); }) } else { reject( self._param ); } } else { reject( self._param ); }}}} At this point, a simple version of the promise is complete.Copy the code

Special thanks to

Read many versions of the promise, feel that the article’s promise is best understood. That draws on the thinking and thank him: segmentfault.com/a/119000001…

The project address

Git:

Github.com/zhuangweizh…