Promise的使用及原理(二):promise的链式调用及测试

3、promise的链式调用
  • 在上面我们已经对异步处理进行优化,但是我们知道promise主要是为了解决回调地狱的问题,那么就涉及到链式调用,很明显,我们上面的代码还不满足链式调用;如果需要满足链式调用,那么then方法中应当返回一个promise

  • 首先,我们先来看一个promise链式调用的简单demo,其中name.txt存放的是"./age.txt",age.txt存放的是"age is 9;"

    let fs = require('fs');
    
    function readFile(url) {
        return new Promise((resolve,reject) => {
            fs.readFile(url,'utf8',(err, data) => {
                if (err){
                    reject(err);
                }
                resolve(data);
            })
        })
    }
    
    readFile('./name.txt').then((data) => {
        return readFile(data);
    }).then((data) => {
        console.log('success--------'+data)
    }).catch(err => {
        console.log('>>>>>>>>>>>>>>>>'+err);
        throw new Error('error')
    });
    
    // 输出为:success--------age is 9;
  • 其次:我们对官方的promise中的then的返回进行分析:
promise2 = promise1.then(onFulfilled, onRejected);
  1. then的返回值必须是一个promise
  2. 如果onFulfilled 或 onRejected返回一个普通值,则运行下面的promise解决过程[[Resolve]](promise2, x)
  3. 如果onFulfilled 或 onRejected抛出异常e,那么会走向promise2的reject方法,并传入参数e
  4. 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
  5. 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因
  • 了解了then的返回值,但是如果要完善自身的promise,我们还应当对Promise的解决过程有一定的了解,即解决onFulfilled 或 onRejected返回值x与promise2的关系:
  1. 如果x与promise2指的是同一个对象,那么以TypeError为原因执行promise2的reject
  2. 如果x是一个promise,那么接受它的状态
    • 如果x处于pending状态,promise2必须保持pending状态直到x执行或被拒绝
    • 如果x处于执行状态,那么以相同的值执行promise2
    • 如果x处于拒绝状态,那么以相同的原因拒绝promise2
  3. 如果x是一个对象或者函数:
    • 把x.then赋值给then;
    • 如果获取x.then时抛出异常e,那么以e为reason拒绝promise2
    • 如果此时获取的then是一个函数,那么将函数中的this指向x,其第一个参数为resolvePromise,第二个参数为rejectPromise
      • 如果resolvePromise被调用,并有参数y,那么则执行[[Resolve]](promise, y)
      • 如果rejectPromise被调用,并有reason-r,那么则以r为参数拒绝promise
      • 如果resolvePromise 和 rejectPromise 均被调用,或同一个参数被调用多次,那么则以第一次调用为优先,之后的调用则被忽略
      • 如果调用then方法时抛出了异常e:
        • 如果resolvePromise 或者 rejectPromise已经被调用,那么忽略它
        • 否则以e为拒因来决绝promise
    • 如果then不是一个函数,那么执行promise,其参数为x
  4. 如果x不是一个对象或者方法,那么执行promise,其参数为x
    代码如下:
class Promise {
    constructor(fn){
        this.data = undefined;   // 回调成功的data
        this.error = undefined;  // 失败回调的error
        this.status = "pending";
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        // 下面在fn中传入resolve和reject方法时,需要进行this绑定,否则在resolve和reject方法中获取到的this为undefined
        fn(this.resolve.bind(this), this.reject.bind(this));
    }
    resolve(data){
        if (this.status === "pending") {
            // 重置状态为fulfilled,并置this.data为获取的数据
            this.status = "fulfilled";
            this.data = data;
            this.onResolvedCallbacks.forEach(fn => {
                fn();
            })
        }
    }
    reject(error){
        // 重置状态为rejected,并置this.error为错误信息
        if (this.status === "pending") {
            this.status = "rejected";
            this.error = error;
            this.onRejectedCallbacks.forEach(fn => {
                fn();
            })
        }
    }
    then(onfulfilled,onrejected){
        // 执行then方法后,应返回一个promise,以便进行链式调用
        let promise2 = new Promise((resolve, reject) => {
            // 当状态为fulfilled时,执行成功的回调
            if(this.status === "fulfilled"){
                // 使用setTimeout是为了防止,在里面使用promise2的时候,promise2还没有值
                setTimeout(() => {
                    // 使用try,catch是为了,如果onFulfilled 或 onRejected抛出异常e,那么会走向promise2的reject方法,并传入参数e
                    try{
                        let x = onfulfilled(this.data);  //  获取onfulfilled的返回值
                        resolvePromise(promise2, x, resolve, reject);
                    }catch(e){
                        reject(e)
                    }
                }, 0);
            }
            // 当状态为rejected时,执行成功的回调
            if (this.status === "rejected"){
               setTimeout(() => {
                   try{
                       let x = onrejected(this.error);     //  获取onrejected的返回值
                       resolvePromise(promise2, x, resolve, reject);
                   }catch(e){
                       reject(e);
                   }
               }, 0);
            }
            // 当状态为pending时,那么将onfulfilled与onrejected方法进行存储,在执行resolve或者reject时进行执行
            if(this.status === "pending"){
                // 这里push onfulfilled 与 onrejected 方法时,不直接push,而是push一个函数,在函数中执行onfulfilled 与 onrejected是为了获取resolve或者reject函数中传递的data或者error
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try{
                            let x = onfulfilled(this.data);
                            resolvePromise(promise2, x, resolve, reject);
                        }catch(e){
                            reject(e);
                        }
                    }, 0);
                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try{
                            let x = onrejected(this.error);
                            resolvePromise(promise2, x, resolve, reject);
                        }catch(e){
                            reject(e);
                        }
                    }, 0);
                });
            }
        });
        return promise2;
    }
}

// Promise的解决过程
function resolvePromise(promise2, x, resolve, reject){
    // 这里的判断是为了防止then中的返回值是promise本身,否则会造成死循环,因此当返回的是promise本身,那么抛出类型错误
    if (promise2 === x){
        return reject(new TypeError("Chaining cycle detected for promise"));
    }
    let called;   // 用于判断是否被多次调用
    if((x !== null && typeof x === "object") || typeof x === "function"){
        try{
            let then = x.then;      // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
            if (typeof then === "function"){
                // 如果 then 是函数,将 x 作为函数的作用域 this 调用之;传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
                then.call(x, y => {
                    if (called) return ;
                    called = true;
                    // 继续对y的返回进行判断
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {
                    if (called) return ;
                    called = true;
                    reject(r);
                })
            }else {
                // then的返回值是一个普通对象,将值传递给resolve
                resolve(x);
            }
        }catch(e){
            if (called) return ;
            called = true;
            reject(e);
        }
    }else {
        // 如果then的返回值为普通值,那么将这个值传递给下一个then的resolve函数中
        resolve(x);
    }
}

module.exports = Promise;
4、promise测试
  • 在对我们自己写的promise进行测试之前,还有一个问题的存在。上面我们提到过,如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值;如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因;
  • 我们首先看下面的例子:
    let p = new Promise((resolve, reject) => {
       resolve("成功执行啦~~")
    });
    
    p.then().then().then(data => {
        console.log(data);
    },err => {
        console.log("err---------------"+err);
    });
  • 运行上面例子可知,在前两个then中,是没有onFulfilled这个函数的,此时,会一直向下执行,并且会返回相同的值;但如果使用我们写的promise时,则会报如下错误:
    err---------------TypeError: onrejected is not a function
  • 这是由于在我们自己写promise中,onFulfilled与onRejected都不为函数,此时,如果运行的话,这两个参数由于都不是函数,但在then方法中,当我们其进行执行时,便会报错:onrejected is not a function,那么就会走到对应的catch中去,也就会执行reject,因此也就出现了上面的报错
  • 如果要解决这个问题,需要对onFulfilled与onRejected不是函数时进行处理,并令其成为一个函数,在then方法开始执行时添加如下代码:
        onfulfilled = typeof onfulfilled === "function" ? onfulfilled : data => data;
        // 注:这里不能返回一个err,如果返回err的话,那么会进入下一个promise的resolve,而非reject
        onrejected = typeof onrejected === "function" ? onrejected : err => {throw err};
  • 最后我们还有一个小功能没有实现,就是promise的catch方法,catch 其实是 then(undefined, () => {}) 的语法糖,因此这个方法比较简单,如下所示:
    catch(onrejected){
        this.then(undefined,onrejected);
    }
  • 以上我们基本完成了promise的功能,下面通过promises-tests来对我们写的promise进行测试。但在测试之前官方文档有如下提到:

In order to test your promise library, you must expose a very minimal adapter interface. These are written as Node.js modules with a few well-known exports:

  1. resolved(value): creates a promise that is resolved with value.
  2. rejected(reason): creates a promise that is already rejected with reason.
  3. deferred(): creates an object consisting of { promise, resolve, reject }:
  • promise is a promise that is currently in the pending state.
  • resolve(value) resolves the promise with value.
  • reject(reason) moves the promise from the pending state to the rejected state, with rejection reason reason.
  • 上面的意思是说:为了测试我们自己的promise库,需要暴露一些小的适配接口。这些是作为Node.js模块所写的,其中有一些众所周知的导出;其中resolved 和 rejected都为可选的,如果他们不存在,那么会在测试运行时使用deferred来进行自动创建,下面我们根据上面描述实现deferred
    Promise.deferred = function(){
        let dfd = {};
        dfd.promise = new Promise((resolve, reject) => {
            dfd.resolve = resolve;
            dfd.reject = reject;
        });
        return dfd;
    };
  • 最后一步,我们就要对promise进行测试啦~

    • promises-aplus-tests安装:npm install -g promises-aplus-tests
    • 进入promise.js所在的目录运行:promises-aplus-tests .\promise.js
    • 测试成功~
  • 完整代码如下:

class Promise {
    constructor(excutor){
        this.data = undefined;   // 回调成功的data
        this.error = undefined;  // 失败回调的error
        this.status = "pending";
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        // 下面在fn中传入resolve和reject方法时,需要进行this绑定,否则在resolve和reject方法中获取到的this为undefined
        try{ // 为了防止在执行promise时抛出异常
            excutor(this.resolve.bind(this), this.reject.bind(this));
        }catch(e){
            this.reject.call(this, e);
        }
    }
    resolve(data){
        if (this.status === "pending") {
            // 重置状态为fulfilled,并置this.data为获取的数据
            this.status = "fulfilled";
            this.data = data;
            this.onResolvedCallbacks.forEach(fn => {
                fn();
            })
        }
    }
    reject(error){
        // 重置状态为rejected,并置this.error为错误信息
        if (this.status === "pending") {
            this.status = "rejected";
            this.error = error;
            this.onRejectedCallbacks.forEach(fn => {
                fn();
            })
        }
    }
    then(onfulfilled,onrejected){
        onfulfilled = typeof onfulfilled === "function" ? onfulfilled : data => data;
        // 注:这里不能返回一个err,如果返回err的话,那么会进入下一个promise的resolve,而非reject
        onrejected = typeof onrejected === "function" ? onrejected : err => {throw err};
        // 执行then方法后,应返回一个promise,以便进行链式调用
        let promise2 = new Promise((resolve, reject) => {
            // 当状态为fulfilled时,执行成功的回调
            if(this.status === "fulfilled"){
                // 使用setTimeout是为了防止,在里面使用promise2的时候,promise2还没有值
                setTimeout(() => {
                    // 使用try,catch是为了,如果onFulfilled 或 onRejected抛出异常e,那么会走向promise2的reject方法,并传入参数e
                    try{
                        let x = onfulfilled(this.data);  //  获取onfulfilled的返回值
                        resolvePromise(promise2, x, resolve, reject);
                    }catch(e){
                        reject(e)
                    }
                }, 0);
            }
            // 当状态为rejected时,执行成功的回调
            if (this.status === "rejected"){
               setTimeout(() => {
                   try{
                       let x = onrejected(this.error);     //  获取onrejected的返回值
                       resolvePromise(promise2, x, resolve, reject);
                   }catch(e){
                       reject(e);
                   }
               }, 0);
            }
            // 当状态为pending时,那么将onfulfilled与onrejected方法进行存储,在执行resolve或者reject时进行执行
            if(this.status === "pending"){
                // 这里push onfulfilled 与 onrejected 方法时,不直接push,而是push一个函数,在函数中执行onfulfilled 与 onrejected是为了获取resolve或者reject函数中传递的data或者error
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try{
                            let x = onfulfilled(this.data);
                            resolvePromise(promise2, x, resolve, reject);
                        }catch(e){
                            reject(e);
                        }
                    }, 0);
                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try{
                            let x = onrejected(this.error);
                            resolvePromise(promise2, x, resolve, reject);
                        }catch(e){
                            reject(e);
                        }
                    }, 0);
                });
            }
        });
        return promise2;
    }
    catch(onrejected){
        this.then(undefined,onrejected);
    }
}

// Promise的解决过程
function resolvePromise(promise2, x, resolve, reject){
    // 这里的判断是为了防止then中的返回值是promise本身,否则会造成死循环,因此当返回的是promise本身,那么抛出类型错误
    if (promise2 === x){
        return reject(new TypeError("Chaining cycle detected for promise"));
    }
    let called;   // 用于判断是否被多次调用
    if((x !== null && typeof x === "object") || typeof x === "function"){
        try{
            let then = x.then;      // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
            if (typeof then === "function"){
                // 如果 then 是函数,将 x 作为函数的作用域 this 调用之;传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
                then.call(x, y => {
                    if (called) return ;
                    called = true;
                    // 继续对y的返回进行判断
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {
                    if (called) return ;
                    called = true;
                    reject(r);
                })
            }else {
                // then的返回值是一个普通对象,将值传递给resolve
                resolve(x);
            }
        }catch(e){
            if (called) return ;
            called = true;
            reject(e);
        }
    }else {
        // 如果then的返回值为普通值,那么将这个值传递给下一个then的resolve函数中
        resolve(x);
    }
}

Promise.deferred = function(){
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
};
    
module.exports = Promise;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值