手把手教你根据PromiseA+手写一个Promise类

什么是Promise

Promise 是一个构造函数, new Promise 返回一个 promise对象 接收一个excutor执行函数作为参数, excutor有两个函数类型形参resolve reject
其用法是
let p = new Promise(function (resolve,reject){
    resolve("这是promise")
    reject("这个不执行")

})
p.then(function (data) {
    console.log(data)
},function (err) {
    console.log(err)
})复制代码

开始写一个Promise

1,我们从用法中就可以看出new Promise里面是一个函数,而函数里的传参是两个函数-resolve和reject。要明白,promise执行的时候有三种状态:

  1. pending
  2. resolve(成功)
  3. reject(失败)

只要成功就不会失败,只要失败就不会成功,不会又成功又失败。走了resolve就不会走reject,写了也没用。promise规范

function Promise(executor) {
    let  _self = this;
    _self.status = "pending";
    _self.value = undefined;//成功的原因;
    _self.reason = undefined;//失败的原因;

    function resolve(value) {
        if(_self.status === "pending"){
            _self.status = "resolved" //改变状态值
            _self.value = value   //给参数赋值
        }
    };
    function reject(reason) {
        _self.status = "rejected"
        _self.reason = reason
    }
    try{  //捕获异常
        executor(resolve,reject); //promise 参数是一个函数,函数中的俩个参数是两个函数
    }catch (e){
       reject(e)
    }
}
//promise实例上有个then方法。
Promise.prototype.then = function (onFulfilled,onRejected) {
    let _sele = this;
    if(_sele.status === "resolved"){
        onFulfilled(_sele.value)
    }
    if(_sele.status === "rejected"){
        onRejected(_sele.reason)
    }

}

module.exports = Promise;复制代码
一个简单的promise模块就写好了引入运行一下
let Promise = require('./promise')
let p = new Promise(function (resolve,reject){
    resolve("这是promise")

})
p.then(function (data) {
    console.log(data)
},function (err) {
    console.log(err)
})复制代码

2,如果resolve 是需要异步执行,如下

let Promise = require('./promise')
let p = new Promise(function (resolve,reject){
    setTimeout(function () {  //给一个延迟
        resolve("这是promise")
    })


})
p.then(function (data) {
    console.log(data)
},function (err) {
    console.log(err)
})复制代码
这样的话执行结果是空,没有执行出来,因为我们类里写的then方法是同步执行,当执行到判断status的值的时候,因为setTimeout的原因,状态值还没改变过来,所以代码会一直停在那,所以我们在这种情况下就要给一个空间来存放这些没有状态的操作,当状态明确了再执行。那么听起来就需要给一个数组结构空间了。
function Promise(executor) {
    let  _self = this;
    _self.status = "pending";
    _self.value = undefined;//成功的原因;
    _self.reason = undefined;//失败的原因;
    _self.onResolvedCallbacks = []; // 存放then成功的回调
    _self.onRejectedCallbacks = []; // 存放then失败的回调

    function resolve(value) { // 成功状态
        if(_self.status === "pending"){
            _self.status = "resolved";
            _self.value = value;
            //判断好了状态,要循环执行出来,这里为什么会是数组,是因为会有:p.then();p.then(),多次执行的状态,不过只要有一个成功就是成功
            _self.onResolvedCallbacks.forEach(function (fn) {
                fn()
            })
        }
    };
    function reject(reason) { // 失败状态
        _self.status = "rejected";
        _self.reason = reason;
         _self.onRejectedCallbacks.forEach(function (fn) {
             fn()
         })
    }
    try{
        executor(resolve,reject);
    }catch (e){ // 捕获的时候发生异常,就直接失败了
       reject(e)
    }
}
//promise实例上有个then方法。
Promise.prototype.then = function (onFulfilled,onRejected) {
    let _sele = this;
    if(_sele.status === "resolved"){
        onFulfilled(_sele.value)
    }
    if(_sele.status === "rejected"){
        onRejected(_sele.reason)
    }
    if(_sele.status === "pending"){
        //把这个状态的都保存起来
        _sele.onResolvedCallbacks.push(function () {
            onFulfilled(_sele.value)
        })
        _sele.onRejectedCallbacks.push(function () {
            onRejected(_sele.reason)
        })
    }

}

module.exports = Promise;复制代码
3,链式调用,promise可以实现链式调用。方便又实惠。执行代码如下:
let promise = new Promise(function (resolve,reject) {
    resolve(100)

})
//链式调用的特点,将第一次成功的返回值当成下一次回调函数的参数
promise.then(function (data) {
  throw new Error("123")
},function () {

}).then(function (data) {
    console.log(data);
},function(err){
    console.log(err)
})

//链式调用:靠的是返回一个新的promise复制代码
但其实也可以这样写:
let p2 = promise.then(function (data) {
    return data  //这里返回的只要不是错误,下次走的还是成功,
},function () {

})
p2.then(function (data) {
    console.log(data);
},function(err){
    console.log(err)
})复制代码

这里不等价于(返回数据的化直接让下一次的promise 成功 如果返回的是promise就等待这个promise执行结果)

let p2 = promise.then(function (data) {
    return new Promise(function (resolve,reject) {
        resolve(data)
    })
},function () {

})
p2.then(function (data) {
    console.log(data);
},function(err){
    console.log(err)
})复制代码
正确的执行结果应该是100,那么开始写吧,(重头戏)
function Promise(executor) {
    let  _self = this;
    _self.status = "pending";
    _self.value = undefined;//成功的原因;
    _self.reason = undefined;//失败的原因;
    _self.onResolvedCallbacks = []; // 存放then成功的回调
    _self.onRejectedCallbacks = []; // 存放then失败的回调

    function resolve(value) { // 成功状态
        if(_self.status === "pending"){
            _self.status = "resolved";
            _self.value = value;
            //判断好了状态,要循环执行出来,这里为什么会是数组,是因为会有:p.then();p.then(),多次执行的状态,不过只要有一个成功就是成功
            _self.onResolvedCallbacks.forEach(function (fn) {
                fn()
            })
        }
    };
    function reject(reason) { // 失败状态
        _self.status = "rejected";
        _self.reason = reason;
         _self.onRejectedCallbacks.forEach(function (fn) {
             fn()
         })
    }
    try{
        executor(resolve,reject);
    }catch (e){ // 捕获的时候发生异常,就直接失败了
       reject(e)
    }
}
//处理返回结果
 function resolvePromise(promise2,x,resolve,reject) {
//
    if(promise2 === x){  //首先要判断返回的x不能跟原来的promise相等,如果是的话就是自己等待自己了!。
       return reject(new TypeError("不能循环引用")); //这里应该报一个类型错误,有问题
    }
    let called; //表示是否调过
    if(x !== null &&(typeof x ==='object'||typeof x ==='function')){ //判断返回的x是不是一个promise,promise应该是一个对象;这里要把null排除因为typeof null是object
        try{
            //  1,看看返回的这个对象或方法里有没有then方法
            let then = x.then;//这里then可能是{then:1}
            if(typeof then  === 'function'){  //说明是一个promise
                then.call(x,function (y) {  //call是为了this指向x,
                    //2,再看是否被调用过
                    if(called) return ; //表示是否调用过成功或者失败
                    called = true;
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2,y,resolve,reject)
                },function (err) { //表示失败了
                    if(called) return ;
                    called = true;
                    reject(err)
                })
            }else{
                resolve(x)
            }
        } catch (e){
            if(called) return ;
            called = true;
            reject(e)
        }

    }else{
        resolve(x)  //如果是常量就直接resolve;
    }
}


//promise实例上有个then方法。
Promise.prototype.then = function (onFulfilled,onRejected) {
    let _sele = this;
    //首先要先定义一个新的promise
    let promise2;
    if(_sele.status === "resolved"){
        promise2 = new Promise(function (resolve,reject) {
            setTimeout(function () {  //为了测试时 效果统一 因为很多人的实现 onfufiled 和 onrejected是异步执行,为了保证测试统一 所以全部异步
                try {   //捕获异常
                    let x =  onFulfilled(_sele.value)  //声明一个x来接受返回值
                    resolvePromise(promise2,x,resolve,reject)
                }catch (e){
                    reject(e)
                }
            })

        })

    }
    if(_sele.status === "rejected"){
        promise2 = new Promise(function (resolve,reject) {
            setTimeout(function () {  //为了测试时 效果统一 因为很多人的实现 onfufiled 和 onrejected是异步执行,为了保证测试统一 所以全部异步
                try {   //捕获异常
                    let x =   onRejected(_sele.reason)  //声明一个x来接受返回值
                    resolvePromise(promise2,x,resolve,reject)
                }catch (e){
                    reject(e)
                }
            })

        })

    }
    // 当调用then时可能没成功 也没失败
    if(_sele.status === "pending"){
        promise2 = new Promise(function (resolve,reject) {
            //把这个状态的都保存起来
            _sele.onResolvedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onFulfilled(_sele.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                })
            });
            _sele.onRejectedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onRejected(_sele.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            });
        })

    }
    return promise2;
}

module.exports = Promise;复制代码

对于promise2 === x 什么情况下出现

var p2 = promise.then(function (data) {
    return p2
},function () {

})
p2.then(function (data) {
    console.log(data);
},function(err){
    console.log(err)
})复制代码

4,如果有人这样执行代码也要考虑进去

let p2 = promise.then(function (data) {
    return new Promise(function (resolve,reject) {
        resolve(data)
    })
},function () {

})
p2.then().then().then(function (data) {//多个then()空调用
    console.log(data);
},function(err){
    console.log(err)
})复制代码
那我们就要对类里then方法里的回调做判断了
        if(_self.status === "pending"){function Promise(executor) {
    let  _self = this;
    _self.status = "pending";
    _self.value = undefined;//成功的原因;
    _self.reason = undefined;//失败的原因;
    _self.onResolvedCallbacks = []; // 存放then成功的回调
    _self.onRejectedCallbacks = []; // 存放then失败的回调

    function resolve(value) { // 成功状态
// value可能是别人的promise
        if(value !==null&&(typeof value === 'object'||typeof value === 'function')){  //这种情况是为了判断 resolve(New promise)
                return value.then(resolve,reject)

            }
        if(_self.status === "pending"){
            _self.status = "resolved";
            _self.value = value;
            //判断好了状态,要循环执行出来,这里为什么会是数组,是因为会有:p.then();p.then(),多次执行的状态,不过只要有一个成功就是成功
            _self.onResolvedCallbacks.forEach(function (fn) {
                fn()
            })
        }
    };
    function reject(reason) { // 失败状态
        _self.status = "rejected";
        _self.reason = reason;
         _self.onRejectedCallbacks.forEach(function (fn) {
             fn()
         })
    }
    try{
        executor(resolve,reject);
    }catch (e){ // 捕获的时候发生异常,就直接失败了
       reject(e)
    }
}
//处理返回结果
 function resolvePromise(promise2,x,resolve,reject) {
    if(promise2 === x){  //首先要判断返回的x不能跟原来的promise相等。
       return reject(new TypeError("不能循环引用")); //这里应该报一个类型错误,有问题
    }
    let called; //表示是否调过
    if(x !== null &&(typeof x ==='object'||typeof x ==='function')){ //判断返回的x是不是一个promise,promise应该是一个对象;这里要把null排除因为typeof null是object
        try{
            //  1,看看返回的这个对象或方法里有没有then方法
            let then = x.then;//这里then可能是{then:1}
            if(typeof then  === 'function'){  //说明是一个promise
                then.call(x,function (y) {  //call是为了this指向x,
                    //2,再看是否被调用过
                    if(called) return ; //表示是否调用过成功或者失败
                    called = true;
                    // y可能还是一个promise,在去解析直到返回的是一个普通值
                    resolvePromise(promise2,y,resolve,reject)
                },function (err) { //表示失败了
                    if(called) return ;
                    called = true;
                    reject(err)
                })
            }else{
                resolve(x)
            }
        } catch (e){
            if(called) return ;
            called = true;
            reject(e)
        }

    }else{
        resolve(x)  //如果是常量就直接resolve;
    }
}


//promise实例上有个then方法。
Promise.prototype.then = function (onFulfilled,onRejected) {
    //成功和失败默认不穿给一个函数
    onFulfilled = typeof onFulfilled ==='function'?onFulfilled:function (value) {
        return value
    };
    onRejected = typeof  onRejected ==='function' ? onRejected :function (err) {
        throw err
    }
    let _sele = this;
    //首先要先定义一个新的promise
    let promise2;
    if(_sele.status === "resolved"){
        promise2 = new Promise(function (resolve,reject) {
            setTimeout(function () {  //为了测试时 效果统一 因为很多人的实现 onfufiled 和 onrejected是异步执行,为了保证测试统一 所以全部异步
                try {   //捕获异常
                    let x =  onFulfilled(_sele.value)  //声明一个x来接受返回值
                    resolvePromise(promise2,x,resolve,reject)
                }catch (e){
                    reject(e)
                }
            })

        })

    }
    if(_sele.status === "rejected"){
        promise2 = new Promise(function (resolve,reject) {
            setTimeout(function () {  //为了测试时 效果统一 因为很多人的实现 onfufiled 和 onrejected是异步执行,为了保证测试统一 所以全部异步
                try {   //捕获异常
                    let x =   onRejected(_sele.reason)  //声明一个x来接受返回值
                    resolvePromise(promise2,x,resolve,reject)
                }catch (e){
                    reject(e)
                }
            })

        })

    }
    // 当调用then时可能没成功 也没失败
    if(_sele.status === "pending"){
        promise2 = new Promise(function (resolve,reject) {
            //把这个状态的都保存起来
            _sele.onResolvedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onFulfilled(_sele.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                })
            });
            _sele.onRejectedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onRejected(_sele.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            });
        })

    }
    return promise2;
}

module.exports = Promise;复制代码
好了,一个完整的promise写完了,同学们联系的时候一定要记得引入自己写的promise文件哦!

对于以上代码实现,我们可以通过一个promises-aplus-tests的库来验证是否达成规范要求

可以通过npm install -g promises-aplus-tests,使用时直接promises-aplus-tests 文件名即可。

在Promise类上的挂方法

实现promise类上的 call,catch,race,resolve,reject等方法

// 捕获错误的方法
Promise.prototype.catch = function (callback) {
    return this.then(null, callback)
}

// 解析全部方法
// let arr = [];
// arr[1] = 100;
// console.log(arr.length)
Promise.all = function (promises) {
    //promises是一个promise的数组
    return new Promise(function (resolve, reject) {
        let arr = []; //arr是最终返回值的结果
        let i = 0; // 表示成功了多少次
        function processData(index, y) {
            arr[index] = y;
            if (++i === promises.length) {
                resolve(arr);
            }
        }
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (y) {
                processData(i, y)
            }, reject)
        }
    })
}

// 只要有一个promise成功了 就算成功。如果第一个失败了就失败了
Promise.race = function (promises) {
    return new Promise(function (resolve, reject) {
        for (var i = 0; i < promises.length; i++) {
            promises[i].then(resolve,reject)
        //这里详细解释一下
        //promise[i].then(function(data){
        //            resolve(data)  //只接收一个resolve,因为他们同属于最外层的new promise。
        //        },reject)
        }
    })
}

// 生成一个成功的promise
Promise.resolve = function(value){
    return new Promise(function(resolve,reject){
        resolve(value);
    })
}

// 生成一个失败的promise
Promise.reject = function(reason){
    return new Promise(function(resolve,reject){
        reject(reason);
    })
}

Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}复制代码
用法如下
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}

let Promise = require('./Promise');
function read() {
    let defer = Promise.defer()
    require('fs').readFile('./2.promise/1.txt', 'utf8', function (err, data) {
        if(err) defer.reject(err);
        defer.resolve(data);
    })
    return defer.promise;
}
read().then(function (data) {
    console.log(data)
},function(err){
    console.log(err);
})
复制代码











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值