JS案例:实现Promise

简介: JS案例:实现Promise

说到ES6的Promise,大家并不陌生,它是JS中解决异步的方法之一

其优点:避免回调地狱,链式调用,函数思路清晰,逻辑相对回调函数和事件发布/订阅更强

缺点:理解性差,异步操作在promise构造函数内部


这段时间在整理面试题时发现,Promise的实现可以单独拿出来分享,于是自己简单实现了一下

码云地址:https://gitee.com/DieHunter/myCode/tree/master/Promise%E5%B0%81%E8%A3%85


实现完整功能之前,我们先了解一下Promise的用法,并实现一个简单的Promise和Promise.then函数

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

上述配置提示中显示,Promise需要传入一个回调函数,函数有两个参数(resolve, reject),第一个是异步执行成功后回调,另一个是失败时的回调。Promise.then方法是执行异步函数成功,即resolve执行时,才会执行then方法中的回调,以下是Promise最简单的一个用法

        new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve('success')// 传参
            }, 500)
        }).then(function (res) {
            console.log(res) // success
        })

下面,我们实现一个最简单的Promise,用于解析Promise.then的原理,主要原理就是用两个回调函数嵌套,将函数作为参数放入至异步操作中,当异步操作执行后再执行作为参数的回调

        function MyPromise(fn) { // 主要原理就是用两个回调函数嵌套,将函数作为参数放入至异步操作中,当异步操作执行后再执行作为参数的回调
            var _this = this;
            _this.params = null; // 传递的参数
            _this.tempResolve = null // _this.tempResolve的作用是将参数传递至then方法中
            function resolve(params) { // 异步操作之后才会执行该方法,执行前一直等待
                _this.params = params
                _this.tempResolve(_this.params)
            }
            fn(resolve) // 将resolve通过回调返回到异步操作函数中,当resolve执行时,才是异步操作执行后
        }
        MyPromise.prototype.then = function (_resolve) { // 异步操作传递参数,简言之就是连接then和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
            })

如果理解了上面的代码,就已经成功了一半,接下来,我们对Promise进行深入的实现,与上述代码差别是,添加then的链式调用,其实可以理解为多层Promise嵌套,但是我们需要对每层Promise做出操作,所以,我们在每层promise中添加status用于记录当前promise是否已执行,tempResolve也要改成tempResolveList,因为需要执行的函数不止一个,变成了一个队列,在上面代码的基础上,我们对resolve进行优化

            function resolve(params) { // 异步操作之后才会执行该方法,执行前一直等待
                if (_this.status === 'pending') {
                    _this.status = 'resolve'; // 进入函数后,立即修改函数状态,防止下面的循环重复执行函数
                    _this.params = params;
                    for (var i = 0; i < array.length; i++) {
                        _this.tempResolveList[i](_this.params) // 执行所有then的链式调用的函数
                    }
                }
            }

除此之外,在then函数中,还需要添加一段代码,其目的是将Promise返回到下一层链式调用,将回调函数通过resolve传递至下一层,达到依次同步执行的目的

        MyPromise.prototype.then = function (tempResolve) { // 异步操作传递参数,简言之就是连接then和resolve
            var _this = this
            var _promise = new MyPromise(function (resolve, reject) {
                if (_this.status == 'pending') {
                    _this.tempResolveList.push(function () {
                        resolve(tempResolve(_this
                            .params)) // 将上一层tempResolve通过resolve的参数异步传递给下一层的Promise中,每层都会异步叠加
                    })
                }
            })
            return _promise // 返回Promise用于链式调用
        }

完成之后,我们会发现一个问题,当我们通过resolve传递tempResolve执行结果时,只有一层链式调用的话,返回的是原回调函数,当到了第二层时,返回的是上一层的resolve,此时我们需要在resolve函数之前做个过滤,并且把参数中的then放在本层,直接执行

                if (params && typeof params === 'function' || typeof params ===
                    'object') { // 这里要判断参数是普通参数params,还是MyPromise方法,链式调用一定会产生MyPromise构造函数
                    var _then = params.then // 如果参数是MyPromise构造函数,则将上层的then放到本层继续执行后续操作
                    if (typeof _then === 'function') {
                        _then.call(params, resolve); // 链式调用then
                        return;
                    }
                }

Promise.then的链式调用完整代码

        function MyPromise(fn) { // 主要原理就是用两个回调函数嵌套,将函数作为参数放入至异步操作中,当异步操作执行后再执行作为参数的回调
            var _this = this;
            _this.status = 'pending'; // 每层Promise的待定状态,只有当前Promise处于pending的时候,才会执行异步函数
            _this.params = null; // 传递的参数
            _this.tempResolveList = new Array() // 储存链式调用then中的函数队列
            function resolve(params) { // 异步操作之后才会执行该方法,执行前一直等待
                if (params && typeof params === 'function' || typeof params ===
                    'object') { // 这里要判断参数是普通参数params,还是MyPromise方法,链式调用一定会产生MyPromise构造函数
                    var _then = params.then // 如果参数是MyPromise构造函数,则将上层的then放到本层继续执行后续操作
                    if (typeof _then === 'function') {
                        _then.call(params, resolve); // 链式调用then
                        return;
                    }
                }
                if (_this.status === 'pending') {
                    _this.status = 'resolve'; // 进入函数后,立即修改函数状态,防止下面的循环重复执行函数
                    _this.params = params;
                    for (var i = 0; i < _this.tempResolveList.length; i++) {
                        _this.tempResolveList[i](_this.params) // 执行所有then的链式调用的函数
                    }
                }
            }
            fn(resolve) // 将resolve通过回调返回到异步操作函数中,当resolve执行时,才是异步操作执行后
        }
        MyPromise.prototype.then = function (tempResolve) { // 异步操作传递参数,简言之就是连接then和resolve
            var _this = this
            var _promise = new MyPromise(function (resolve, reject) {
                if (_this.status == 'pending') {
                    _this.tempResolveList.push(function () {
                        resolve(tempResolve(_this
                            .params)) // 将上一层tempResolve通过参数异步传递给下一层的Promise中,每层都会异步叠加
                    })
                }
            })
            return _promise // 返回Promise用于链式调用
        }
        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
            })

实现了链式调用后,我们对reject以及catch进行一个简单的实现,其实现过程与then相似,我们对一些方法封装一下,得到以下代码(catch没有完善链式调用,导致then方法执行数量大于1时失效)

        function MyPromise(fn) { // 主要原理就是用两个回调函数嵌套,将函数作为参数放入至异步操作中,当异步操作执行后再执行作为参数的回调
            var _this = this;
            _this.status = 'pending'; // 每层Promise的待定状态,只有当前Promise处于pending的时候,才会执行异步函数
            _this.params = null; // 传递的参数
            _this.tempResolveList = new Array() // 储存链式调用then中的函数队列
            _this.tempRejectList = new Array() // 储存链式调用catch中的函数队列
            _this.runCommandList = function (_status, _params,
                _commandList) { // 若函数状态是pending待定状态,函数执行后会有两个状态,resolve和reject
                if (_params && typeof _params === 'function' || typeof _params ===
                    'object') { // 这里要判断参数是普通参数params,还是MyPromise方法,链式调用一定会产生MyPromise构造函数
                    var _then = _params.then // 如果参数是MyPromise构造函数,则将上层的then放到本层继续执行后续操作
                    if (typeof _then === 'function') {
                        _then.call(_params, resolve); // 链式调用then
                        return;
                    }
                }
                if (_this.status === 'pending') {
                    _this.status = _status; // 进入函数后,立即修改函数状态,防止下面的循环重复执行函数
                    _this.params = _params;
                    for (var i = 0; i < _commandList.length; i++) {
                        _commandList[i](_this.params) // 执行所有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)) // 将上一层tempResolve通过参数异步传递给下一层的Promise中,每层都会异步叠加
                    }
                })
            }
            function resolve(params) { // 异步操作之后才会执行该方法,执行前一直等待,通过回调返回到new Promise(fn)参数中
                _this.runCommandList('resolve', params, _this.tempResolveList)
            }
            function reject(params) { // 异步操作之后才会执行该方法,执行前一直等待,通过回调返回到new Promise(fn)参数中
                _this.runCommandList('reject', params, _this.tempRejectList)
            }
            try { //捕获异常
                fn(resolve, reject)
            } catch (error) {
                reject(error)
            } // 将resolve通过回调返回到异步操作函数中,当resolve执行时,才是异步操作执行后
        }
        MyPromise.prototype.then = function (tempResolve) { // 异步操作传递参数,简言之就是连接then和resolve
            var _this = this
            var _promise = _this.createPromise(tempResolve, _this.tempResolveList)
            _promise.catch = function (tempReject) { // 异步操作传递参数,简言之就是连接then和resolve
                _this.createPromise(tempReject, _this.tempRejectList)
            }
            return _promise // 返回Promise用于链式调用
        }
        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
            })

总结:代码可能有地方不完善,欢迎大佬指出


相关文章
|
5天前
|
前端开发 JavaScript
用JavaScript 实现一个简单的 Promise 并打印结果
用 JavaScript 实现一个简单的 Promise 并打印结果
|
5天前
|
JSON 前端开发 JavaScript
在 JavaScript 中,如何使用 Promise 处理异步操作?
通过以上方式,可以使用Promise来有效地处理各种异步操作,使异步代码更加清晰、易读和易于维护,避免了回调地狱的问题,提高了代码的质量和可维护性。
|
10天前
|
JSON 前端开发 JavaScript
浅谈JavaScript中的Promise、Async和Await
【10月更文挑战第30天】Promise、Async和Await是JavaScript中强大的异步编程工具,它们各自具有独特的优势和适用场景,开发者可以根据具体的项目需求和代码风格选择合适的方式来处理异步操作,从而编写出更加高效、可读和易于维护的JavaScript代码。
18 1
|
1月前
|
前端开发 JavaScript 开发者
JavaScript 中的异步编程:深入了解 Promise 和 async/await
【10月更文挑战第8天】JavaScript 中的异步编程:深入了解 Promise 和 async/await
|
1月前
|
前端开发 JavaScript 小程序
JavaScript的ES6中Promise的使用以及个人理解
JavaScript的ES6中Promise的使用以及个人理解
16 1
|
2月前
|
前端开发 JavaScript
JavaScript中的Promise:简化异步编程
JavaScript中的Promise:简化异步编程
|
2月前
|
Web App开发 前端开发 JavaScript
js之 Promise | 12-8
js之 Promise | 12-8
|
29天前
|
前端开发 JavaScript UED
深入了解JavaScript异步编程:回调、Promise与async/await
【10月更文挑战第11天】深入了解JavaScript异步编程:回调、Promise与async/await
11 0
|
2月前
|
前端开发 JavaScript
ES6新标准下JS异步编程Promise解读
ES6新标准下JS异步编程Promise解读
36 3
|
1月前
|
前端开发 JavaScript 开发者
深入理解JavaScript中的Promise:用法与最佳实践
【10月更文挑战第8天】深入理解JavaScript中的Promise:用法与最佳实践
55 0