♛前端♛ 手写 Promise 过程

91 阅读3分钟

Promise的手写

Javascript

  1. 首先定义一个Promise的方法,名称任意,传入执行器 executor

    // 传入执行器 executor
    function rfxyPromise(executor) {
        function resolve() {
            console.log("resolve")
        }
        function reject() {
            console.log("reject")
        }
        executor(resolve,reject)
    }
    
  2. 调用该Promise方法

    var p = new rfxyPromise((resolve, reject) => {
        resolve("调用resolve方法")
        //reject("调用reject方法")
    })
    ​
    p.then((res) => {
        console.log(res)
    }, (err) => {
        console.log(err)
    })
    
  3. 设置rfxyPromise函数的 then

    //这个方法仅让浏览器不进行报错
    rfxyPromise.prototype.then = function () {
        
    }
    
  4. 设置 fulfilled 值和 pending

    function rfxyPromise(executor) {
        //初始的 status 值为 pending
        this.status = "pending"
        function resolve() {
            //谁调用this,this就指向谁
            //结果显示,此处的this指向的是window
            console.log(this)
            //设置status为执行后的结果
            this.status = "fulfiled"
        }
        function reject() {
            //设置status为执行后的结果
            this.status = "rejected"
        }
        executor(resolve,reject)
    }
    
  5. 设置一个局部的 this 保存 this

    function rfxyPromise(executor) {
        this.status = "pending"
        var _this = this
        function resolve() {
            console.log(_this)
            _this.status = "fulfiled"
        }
        function reject() {
            _this.status = "rejected"
        }
        executor(resolve,reject)
    }
    
  6. then 添加方法

    rfxyPromise.prototype.then = function (successCB,failCB) {
        //检验返回的是否为 fulfilled 或 rejected,从而决定执行那一步
        if (this.status === "fulfilled") {
            successCB()
        } else if (this.status === "rejected"){
            failCB()
        }
    }
    
  7. 定义 res

    function rfxyPromise(executor) {
        this.status = "pending"
        var _this = this
    ​
        //设置result为undefined
        //result用于存储函数的参数,供successCB和failCB使用
    ​
        var _result = undefined
        function resolve(res) {
            console.log(_this)
            _this.status = "fulfilled"
    ​
            _this.result = res
        }
        function reject(res) {
            _this.status = "rejected"
            _this.result = res
        }
        executor(resolve,reject)
    }
    
  8. 传送 result

    rfxyPromise.prototype.then = function (successCB,failCB) {
        if (this.status === "fulfilled") {
            successCB(this.result)
        } else if (this.status === "rejected"){
            failCB(this.result)
        }
    }
    
  9. 优化细节

    //删掉err传递的错误函数,再删掉res的成功函数,验证是否可以成功
    p.then(res => {
        console.log("success", res)
    })
    ​
    //此处加入判断条件是否成立,决定程序是否会进行报错
    rfxyPromise.prototype.then = function (successCB, failCB) {
        if (this.status === "fulfilled") {
            //验证原型上 .then 中的回调函数是否为真,决定是否能运行
            successCB && successCB(this.result);
        } else if (this.status === "rejected") {
            //验证原型上 .then 中错误的回调函数是否为真,决定是否能运行
            failCB && failCB(this.result);
        }
    };
    
  10. 保持 fulfilledrejected 不会被替代

    function rfxyPromise(executor) {
        this.status = "pending";
        var _this = this;
        var _result = undefined;
        function resolve(res) {
            if (_this.status !== "pending")
            {return;}
            _this.status = "fulfilled";
            _this.result = res;
        }
        function reject(res) {
            if (_this.status !== "pending")
            {return;}
            _this.status = "rejected";
            _this.result = res;
        }
        executor(resolve, reject);
    }
    
  11. 设置一个定时器,用于模拟Ajax请求方式

    var p = new rfxyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve("111")
        }, 1000)
        // reject("32")
    })
    //此时的 .then 方法不会执行,因为当前是 pending 状态,和下面代码没有关系
    rfxyPromise.prototype.then = function (successCB, failCB) {
        if (this.status === "fulfilled") {
            successCB(this.result);
        }
        if (this.status === "rejected") {
            failCB && failCB(this.result);
        }
    ​
        //验证状态:输出pending
        console.log(this.status)
    };
    
  12. 收集回调

    rfxyPromise.prototype.then = function (successCB, failCB) {
        if (this.status === "fulfilled") {
            successCB(this.result);
        }
        if (this.status === "rejected") {
            failCB && failCB(this.result);
        }
        //将回调函数收集在cb中
        if (this.status === "pending") {
            this.cb = {
                successCB,
                failCB,
            };
        }
    };
    
  13. 在Promise方法中添加刚设置的 cb

    function rfxyPromise(executor) {
        this.status = "pending";
        this.result = undefined;
        //此处收集回调函数
        this.cb = {};
    ​
        var _this = this;
    ​
        function resolve(res) {
            if (_this.status !== "pending") {
                return;
            }
            _this.status = "fulfilled";
            _this.result = res;
            //将成功函数调用
            _this.cb.successCB &&_this.cb.successCB()
        }
        function reject(res) {
            if (_this.status !== "pending") {
                return;
            }
            _this.status = "rejected";
            _this.result = res;
            //将失败函数调用
            _this.cb.failCB &&_this.cb.failCB()
        }
        executor(resolve, reject);
    }
    
  14. 将值传递给回调函数,resreq 就可以使用了( 此过程类似于 订阅发布模式

    function rfxyPromise(executor) {
        this.status = "pending";
        this.result = undefined;
        this.cb = {};
    ​
        var _this = this;
    ​
        function resolve(res) {
            if (_this.status !== "pending") {
                return;
            }
            _this.status = "fulfilled";
            _this.result = res;
            //添加_this.result
            _this.cb.successCB &&_this.cb.successCB(_this.result)
        }
        function reject(res) {
            if (_this.status !== "pending") {
                return;
            }
            _this.status = "rejected";
            _this.result = res;
            //添加_this.result
            _this.cb.failCB &&_this.cb.failCB(_this.result)
        }
        executor(resolve, reject);
    }
    
  15. .then 中,异步编程将会覆盖回调函数,回调函数中的 cb 是对象,所以改为使用数组,压栈!

    function rfxyPromise(executor) {
        this.status = "pending";
        this.result = undefined;
        //修改为数组
        this.cb = [];
    ​
        var _this = this;
    ​
        function resolve(res) {
            if (_this.status !== "pending") {
                return;
            }
            _this.status = "fulfilled";
            _this.result = res;
            _this.cb.successCB &&_this.cb.successCB(_this.result)
        }
        function reject(res) {
            if (_this.status !== "pending") {
                return;
            }
            _this.status = "rejected";
            _this.result = res;
            _this.cb.failCB &&_this.cb.failCB(_this.result)
        }
        executor(resolve, reject);
    }
    ​
    rfxyPromise.prototype.then = function (successCB, failCB) {
        if (this.status === "fulfilled") {
            successCB(this.result);
        }
        if (this.status === "rejected") {
            failCB && failCB(this.result);
        }
        if (this.status === "pending") {
            //用push方法进行压栈操作
            this.cb.push({
                successCB,
                failCB
            });
        }
    };
    
  16. 遍历每一个数组

    function rfxyPromise(executor) {
        this.status = "pending";
        this.result = undefined;
        this.cb = [];
    ​
        var _this = this;
    ​
        function resolve(res) {
            if (_this.status !== "pending") {
                return;
            }
            _this.status = "fulfilled";
            _this.result = res;
            //使用forEach进行遍历
            _this.cb.forEach(item => {
                item.successCB && item.successCB(_this.result)
            })
        }
        function reject(res) {
            if (_this.status !== "pending") {
                return;
            }
            _this.status = "rejected";
            _this.result = res;
            //使用forEach进行遍历
            _this.cb.forEach(item => {
                item.failCB &&item.failCB(_this.result)
            })
        }
        executor(resolve, reject);
    }