JS case: Realizing Promise

JS case: Realizing Promise

Speaking of ES6 Promise, everyone is not unfamiliar. It is one of the ways to solve asynchronous in JS.
Its advantages: avoid callback hell, chain calls, clear function ideas, and logic is stronger than callback functions and event publish/subscribe.
Disadvantages: understand Poor performance, asynchronous operation is inside the promise constructor

During this period of time, when sorting out the interview questions, I found that the realization of Promise can be shared separately, so I simply realized it myself.
Code cloud address: gitee.com/DieHunter/m...

Before implementing the complete function, let's first understand the usage of Promise and implement a simple Promise and Promise.then function

Promise (Executor: ( Resolve: (?: the any value) => void , Reject: (the any reason ?:) => void ) => void ): Promise <the any> copy the code

The above configuration prompt shows that Promise needs to pass in a callback function. The function has two parameters (resolve, reject). The first is the callback after the asynchronous execution succeeds, and the other is the callback when it fails. The Promise.then method is to execute the asynchronous function successfully, that is, when the resolve is executed, the callback in the then method will be executed. The following is the simplest usage of Promise

new Promise ( function ( resolve, reject ) { setTimeout ( function () { resolve( 'success' ) //pass parameters ), 500 ) }).then( function ( res ) { console .log(res) //success }) Copy code

Next, we implement the simplest Promise, which is used to resolve the principle of Promise.then. The main principle is to nest two callback functions, put the function as a parameter into the asynchronous operation, and execute it as the parameter after the asynchronous operation is executed. Callback

function MyPromise ( fn ) {//The main principle is to nest two callback functions, put the function as a parameter into the asynchronous operation, and execute the callback as the parameter after the asynchronous operation is executed var _this = this ; _this.params = null ; //The passed parameters _this.tempResolve = null //The function of _this.tempResolve is to pass the parameters to the then method function resolve ( params ) { //The method will be executed after the asynchronous operation, before the execution always waiting _this.params = params _this.tempResolve(_this.params) } fn(resolve) //Return resolve to the asynchronous operation function through a callback. When the resolve is executed, it is after the asynchronous operation is executed } MyPromise.prototype.then = function ( _resolve ) { //Asynchronous operation to pass parameters, in short is to connect then and resolve var _this = this _this.tempResolve = function () { _resolve(_this.params) } } MyPromise.prototype.constructor = MyPromise new MyPromise( function ( res, rej ) { setTimeout ( function () { res( 'success' ) }, 1000 ) }) .then( function ( res ) { console .log(res) //success }) Copy code

If you understand the above code, you have already succeeded in half. Next, we will implement the Promise in depth. The difference from the above code is that adding then chain calls can actually be understood as multi-layer Promise nesting, but we need Operations are performed on each layer of Promise, so we add status to each layer of promise to record whether the current promise has been executed, and tempResolve should also be changed to tempResolveList, because there is more than one function that needs to be executed, and it becomes a queue. Based on the code, we optimize the resolve

function resolve ( params ) {//This method will be executed after asynchronous operation, and wait until execution if (_this.status === 'pending' ) { _this.status = 'resolve' ; //After entering the function, immediately modify the function status to prevent the following loop from repeating the function _this.params = params; for ( var i = 0 ; i <array.length; i++) { _this.tempResolveList[i](_this.params) //The function that executes all then chain calls } } } Copy code

In addition, in the then function, you also need to add a piece of code, the purpose of which is to return the Promise to the next layer of chained calls, and pass the callback function to the next layer through resolve, so as to achieve the purpose of sequential synchronous execution.

MyPromise.prototype.then = function ( tempResolve ) { //Asynchronous operation to pass parameters, in short is to connect then and resolve var _this = this var _promise = new MyPromise( function ( resolve, reject ) { if (_this.status == 'pending' ) { _this.tempResolveList.push( function () { resolve(tempResolve(_this .params)) // Pass the previous layer of tempResolve asynchronously to the next layer of Promise through the resolve parameters, and each layer will be asynchronously superimposed }) } }) return _promise //return Promise for chain call } Copy code

After the completion, we will find a problem. When we pass the execution result of tempResolve through resolve, if there is only one level of chain call, the original callback function is returned. When the second level is reached, the previous level s resolve is returned. At this point we need to do a filter before the resolve function, and put the then in the parameter on this layer, and execute it directly

if (params && typeof params === 'function' || typeof params === 'object' ) { //Here it is necessary to determine whether the parameter is a normal parameter params or a MyPromise method. The chain call will definitely generate the MyPromise constructor var _then = params.then //If the parameter is the MyPromise constructor, put the upper layer then on this layer to continue the subsequent operations if ( typeof _then === 'function' ) { _then.call(params, resolve); //chain call then return ; } } Copy code

Promise.then chain call complete code

function MyPromise ( fn ) {//The main principle is to nest two callback functions, put the function as a parameter into the asynchronous operation, and execute the callback as the parameter after the asynchronous operation is executed var _this = this ; _this.status = 'pending' ; //The pending state of each level of Promise, only when the current Promise is pending, will the asynchronous function be executed _this.params = null ; //the passed parameters _this.tempResolveList = new Array () //Store chained calls to the function queue in then function resolve ( params ) { //This method will be executed after asynchronous operation, and wait until if (params && typeof params === 'function' || typeof params === 'object' ) { //Here it is necessary to judge whether the parameter is a normal parameter params or a MyPromise method. The chain call will definitely produce a MyPromise constructor var _then = params.then//If the parameter is the MyPromise constructor, put the upper layer then on this layer to continue the subsequent operations if ( typeof _then === 'function' ) { _then.call(params, resolve); //chain call then return ; } } if (_this.status === 'pending' ) { _this.status = 'resolve' ; //After entering the function, immediately modify the function status to prevent the following loop from repeating the function _this.params = params; for ( var i = 0 ; i <_this.tempResolveList.length; i++) { _this.tempResolveList[i](_this.params) //The function that executes all then chain calls } } } fn(resolve) //Return resolve to the asynchronous operation function through a callback. When the resolve is executed, it is after the asynchronous operation is executed } MyPromise.prototype.then = function ( tempResolve ) { //Asynchronous operation to pass parameters, in short is to connect then and resolve var _this = this var _promise = new MyPromise( function ( resolve, reject ) { if (_this.status == 'pending' ) { _this.tempResolveList.push( function () { resolve(tempResolve(_this .params)) // Pass the previous layer of tempResolve asynchronously to the next layer of Promise via parameters, and each layer will be asynchronously superimposed }) } }) return _promise //return Promise for chain call } MyPromise.prototype.constructor = MyPromise var count = 1 new MyPromise( function ( res, rej ) { setTimeout ( function () { res( 'success' + count++) }, 1000 ) }) .then( function ( res ) { console .log(res) //success1 return new MyPromise( function ( res, rej ) { setTimeout ( function () { res( 'success' + count++) }, 1000 ) }) }).then( function ( res ) { console .log(res) //success2 return new MyPromise( function ( res, rej ) { setTimeout ( function () { res( 'success' + count++) }, 1000 ) }) }).then( function ( res ) { console .log(res) //success3 }) Copy code

After the chain call is implemented, we implement a simple implementation of reject and catch. The implementation process is similar to then. We encapsulate some methods and get the following code (catch does not perfect the chain call, resulting in the number of executions of the then method being greater than 1 Time expires)

function MyPromise ( fn ) {//The main principle is to nest two callback functions, put the function as a parameter into the asynchronous operation, and execute the callback as the parameter after the asynchronous operation is executed var _this = this ; _this.status = 'pending' ; //The pending state of each level of Promise, only when the current Promise is pending, will the asynchronous function be executed _this.params = null ; //the passed parameters _this.tempResolveList = new Array () //Store the function queue in the chain call then _this.tempRejectList = new Array () //Store the function queue in the chain call catch _this.runCommandList = function ( _status, _params, _commandList ) {//If the function status is pending, there will be two states after the function is executed, resolve and reject if (_params && typeof _params === 'function' || typeof _params === 'object' ) {//Here to determine whether the parameter is a normal parameter params or a MyPromise method, the chain call will definitely generate a MyPromise constructor var _then = _params.then//If the parameter is a MyPromise constructor, put the upper layer then on this layer to continue execution Operation if ( typeof _then === 'function' ) { _then.call(_params, resolve); //chain call then return ; } } if (_this.status === 'pending' ) { _this.status = _status; //After entering the function, immediately modify the function status to prevent the following loop from repeating the function _this.params = _params; for ( var i = 0 ; i <_commandList.length; i++) { _commandList[i](_this.params) //execute all the functions of the then chain call } } } _this.runCallBack = function ( resolve, reject, finishFn ) { return function () { try { var temp = finishFn(_this.params); resolve(temp); } catch (error) { reject(error); } } } _this.createPromise = function ( temp, tempList ) { var _this = this return new MyPromise( function ( resolve, reject ) { if (_this.status == 'pending' ) { tempList.push(_this.runCallBack(resolve, reject, temp)) //Pass the previous layer of tempResolve asynchronously to the next layer of Promise via parameters, and each layer will be asynchronously superimposed } }) } function resolve ( params ) {//This method will be executed after asynchronous operation, wait until execution, and return to the new Promise(fn) parameter via callback _this.runCommandList( 'resolve' , params, _this.tempResolveList) } function reject ( params ) {//This method will be executed after asynchronous operation, wait until execution, and return to the new Promise(fn) parameter via callback _this.runCommandList( 'reject' , params, _this.tempRejectList) } try { //Catch the exception fn(resolve, reject) } catch (error) { reject(error) } //Return the resolve to the asynchronous operation function through the callback. When the resolve is executed, it is after the asynchronous operation is executed } MyPromise.prototype.then = function ( tempResolve ) { //Asynchronous operation to pass parameters, in short is to connect then and resolve var _this = this var _promise = _this.createPromise(tempResolve, _this.tempResolveList) _promise.catch = function ( tempReject ) { //Asynchronous operation passes parameters, in short, connects then and resolve _this.createPromise(tempReject, _this.tempRejectList) } return _promise //return Promise for chain call } MyPromise.prototype.constructor = MyPromise var count = 1 new MyPromise( function ( res, rej ) { setTimeout ( function () { rej( 'success' + count++) }, 1000 ) //setTimeout(function () { //res('success' + count++) //}, 1000) }) .then( function ( res ) { console .log(res) //success1 return new MyPromise( function ( res, rej ) { setTimeout ( function () { res( 'success' + count++) }, 1000 ) }) }).catch( function ( err ) { console .log(err) //success1 }) Copy code

Summary: There may be some imperfections in the code, welcome to point out