ES6 Promise is a familiar Promise, one of the solutions to asynchrony in JS

Advantages: Avoid callback hell, chain calls, function thinking is clear, logic is stronger than callback function and event publish/subscribe

Disadvantages: Poor understanding, asynchronous operations inside the Promise constructor

During this period of time, WHEN I was sorting out the interview questions, I found that the implementation of Promise could be shared separately, so I simply implemented it myself

Code cloud Address:Gitee.com/DieHunter/m…

Before we implement the full functionality, let’s look at the use of Promise and implement a simple Promise and promise.then function

Promise(executor: (resolve: (value? : any) =>void, reject: (reason? : any) =>void) = > void) :Promise<any>
Copy the code

As you can see from the above configuration tip, a Promise needs to be passed in a callback function that takes two arguments (resolve, reject), the first to call back if the asynchronous execution succeeds and the second to call back if it fails. The Promise. Then method executes a callback from the then method only if the asynchronous function is successfully executed. The following is the simplest use of Promise

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

Let’s implement a simple Promise that resolves the Promise. Then principle. The main principle is to nest two callback functions, put the function as parameter in the asynchronous operation, and execute the callback as parameter after the asynchronous operation is executed

        function MyPromise(fn) { // The main principle is to use two nested callback functions, put the function as a parameter in the asynchronous operation, when the asynchronous operation is executed as a parameter callback
            var _this = this;
            _this.params = null; // Parameters to pass
            _this.tempResolve = null // _this.tempResolve is used to pass arguments to the then method
            function resolve(params) { // The method is executed after an asynchronous operation, waiting until it executes
                _this.params = params
                _this.tempResolve(_this.params)
            }
            fn(resolve) // Return resolve via a callback to the asynchronous operation. When resolve executes, the asynchronous operation executes
        }

        MyPromise.prototype.then = function (_resolve) { // Asynchronous operations pass parameters, which in short join 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 the code

If we understand the above code, we are already half way there. Next, we will further implement the Promise. The difference is that we will add the chain call of then. We added status to each layer of promise to record whether or not the current promise has been executed. TempResolve should also be changed to tempResolveList because more than one function needs to be executed, becoming a queue. We optimized resolve based on the code above

            function resolve(params) { // The method is executed after an asynchronous operation, waiting until it executes
                if (_this.status === 'pending') {
                    _this.status = 'resolve'; // After entering the function, modify the function state immediately to prevent the following loop from executing the function repeatedly
                    _this.params = params;
                    for (var i = 0; i < array.length; i++) {
                        _this.tempResolveList[i](_this.params) // The function that executes all chain calls to then}}}Copy the code

In addition, in the THEN function, you need to add a piece of code that returns the Promise to the next level of the chain call and passes the callback to the next level through resolve for sequential synchronized execution

        MyPromise.prototype.then = function (tempResolve) { // Asynchronous operations pass parameters, which in short join 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)) // Asynchronously pass the previous tempResolve through the resolve argument to the next Promise, each layer being superimposed asynchronously})}})return _promise // Return Promise for chained calls
        }
Copy the code

If we pass tempResolve (resolve), we need to filter resolve. If we pass tempResolve (resolve), we need to filter resolve. If we pass tempResolve (resolve), we need to filter resolve. Then is placed in this layer and executed directly

                if (params && typeof params === 'function' || typeof params ===
                    'object') { // If the parameter is params or MyPromise, the chain call must produce the MyPromise constructor
                    var _then = params.then // If the argument is the MyPromise constructor, then is placed in this layer to continue with the subsequent operations
                    if (typeof _then === 'function') {
                        _then.call(params, resolve); // call then
                        return; }}Copy the code

Full code for chain calls to promise.then

        function MyPromise(fn) { // The main principle is to use two nested callback functions, put the function as a parameter in the asynchronous operation, when the asynchronous operation is executed as a parameter callback
            var _this = this;
            _this.status = 'pending'; // The async function is executed only when the current Promise is pending
            _this.params = null; // Parameters to pass
            _this.tempResolveList = new Array(a)// Store the queue of functions in the chained call then
            function resolve(params) { // The method is executed after an asynchronous operation, waiting until it executes
                if (params && typeof params === 'function' || typeof params ===
                    'object') { // If the parameter is params or MyPromise, the chain call must produce the MyPromise constructor
                    var _then = params.then // If the argument is the MyPromise constructor, then is placed in this layer to continue with the subsequent operations
                    if (typeof _then === 'function') {
                        _then.call(params, resolve); // call then
                        return; }}if (_this.status === 'pending') {
                    _this.status = 'resolve'; // After entering the function, modify the function state immediately to prevent the following loop from executing the function repeatedly
                    _this.params = params;
                    for (var i = 0; i < _this.tempResolveList.length; i++) {
                        _this.tempResolveList[i](_this.params) // The function that executes all chain calls to then
                    }
                }
            }
            fn(resolve) // Return resolve via a callback to the asynchronous operation. When resolve executes, the asynchronous operation executes
        }

        MyPromise.prototype.then = function (tempResolve) { // Asynchronous operations pass parameters, which in short join 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)) // In the Promise that passes the previous tempResolve layer to the next layer asynchronously, each layer is superimposed asynchronously})}})return _promise // Return Promise for chained calls
        }
        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 the code

After implementing the chain call, we implement a simple implementation of reject and catch, which is similar to then. We encapsulate some methods and get the following code (Catch fails to perfect the chain call, resulting in the failure of the then method when the number of executions is greater than 1).

        function MyPromise(fn) { // The main principle is to use two nested callback functions, put the function as a parameter in the asynchronous operation, when the asynchronous operation is executed as a parameter callback
            var _this = this;
            _this.status = 'pending'; // The async function is executed only when the current Promise is pending
            _this.params = null; // Parameters to pass
            _this.tempResolveList = new Array(a)// Store the queue of functions in the chained call then
            _this.tempRejectList = new Array(a)// Store the queue of functions in a chain call to catch

            _this.runCommandList = function (_status, _params, _commandList) { // If the function state is pending, the function execution will have two states, resolve and reject
                if (_params && typeof _params === 'function' || typeof _params ===
                    'object') { // If the parameter is params or MyPromise, the chain call must produce the MyPromise constructor
                    var _then = _params.then // If the argument is the MyPromise constructor, then is placed in this layer to continue with the subsequent operations
                    if (typeof _then === 'function') {
                        _then.call(_params, resolve); // call then
                        return; }}if (_this.status === 'pending') {
                    _this.status = _status; // After entering the function, modify the function state immediately to prevent the following loop from executing the function repeatedly
                    _this.params = _params;
                    for (var i = 0; i < _commandList.length; i++) {
                        _commandList[i](_this.params) // The function that executes all chain calls to then
                    }
                }
            }
            _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)) // In the Promise that passes the previous tempResolve layer to the next layer asynchronously, each layer is superimposed asynchronously}})}function resolve(params) { // The method is executed after an asynchronous operation, waiting until it is returned to the new Promise(fn) argument via a callback
                _this.runCommandList('resolve', params, _this.tempResolveList)
            }

            function reject(params) { // The method is executed after an asynchronous operation, waiting until it is returned to the new Promise(fn) argument via a callback
                _this.runCommandList('reject', params, _this.tempRejectList)
            }
            try { // Catch an exception
                fn(resolve, reject)
            } catch (error) {
                reject(error)
            } // Return resolve via a callback to the asynchronous operation. When resolve executes, the asynchronous operation executes
        }

        MyPromise.prototype.then = function (tempResolve) { // Asynchronous operations pass parameters, which in short join then and resolve
            var _this = this
            var _promise = _this.createPromise(tempResolve, _this.tempResolveList)
            _promise.catch = function (tempReject) { // Asynchronous operations pass parameters, which in short join then and resolve
                _this.createPromise(tempReject, _this.tempRejectList)
            }
            return _promise // Return Promise for chained calls
        }



        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 the code

Summary: the code may have some imperfections, welcome to point out