Promise浅析

151 阅读6分钟

前言:来来回回Promise已经学习了很多遍,一直没有成体系的总结过,这次就对promise做个了结,让我们一步步来实现一个Promise库。

首先第一点,我们需要了解Promise的本质:
Promise本质是一个会将传递进入的executor函数立刻执行的用来解决异步问题的类!

Promise基础版

ps:基础版和实际Promise库稍有区别,将在后续讲解完善。(then实质上是微任务)

然后,对比PromiseA+规范我们再来看下Promise的一些特性:

1.promise实例会有一个默认的pending状态,当调用resolve函数时,在函数内将pending状态修改为resolved状态,而调用reject函数时,在函数内部将pending状态修改为rejected状态;

2.一旦已经调用过resolve函数或者reject函数之后,再次调用则状态不会改变,也就是状态改变之后时不可逆的。

3.promise可以调用类的原型上的then方法,then方法接收两个函数,实例状态为resolved时,执行第一个函数,参数为resolve函数的传入值,实例状态为rejected时,执行第二个函数参数,参数为reject函数的传入值。

由此,我们可以得出Promise类基本结构:

function Promise (executor){
    // 在promise内部定义一个状态 当前promise的状态
    let self = this;
    self.value = undefined;
    self.reason = undefined;
    self.status = 'pending'; // 默认promise的状态是pengding
    function resolve(value){
        if(self.status === 'pending'){
            self.value = value;
            self.status = 'resolved'; // 成功态
        }
    }
    function reject(reason){
        if(self.status === 'pending'){
            self.reason = reason;
            self.status = 'rejected'; // 失败态
        }
    }
    executor(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    if(self.status === 'resolved'){
        onFulfilled(self.value);
    }
    if(self.status === 'rejected'){
        onRejected(self.reason);
    }
}
module.exports = Promise;

使用结果如下:

let Promise = require('./promise');
let p = new Promise(function(resolve,reject){
    console.log('start');
    reject('情人到了');
    resolve('情人节到了');
});
// es6的内容
p.then((value)=>{
    console.log('success',value);
},(reason)=>{
    console.log('error',reason);
})
console.log('end');

//start
//error 情人到了
//end

Promise进阶版

在原有基础上,我们需要满足:

1.new Promise中可以包含异步逻辑
2.同一个实例可以多次then

所以我们需要对之前的基础版进行完善:

function Promise (executor){
    // 在promise内部定义一个状态 当前promise的状态
    let self = this;
    self.value = undefined;
    self.reason = undefined
    self.status = 'pending'; // 默认promise的状态是pengding
    self.onResolevedCallbacks = []; // 存放所有成功的回调
    self.onRejectedCallbacks = []; // 存放所有失败的回调
    function resolve(value){
        if(self.status === 'pending'){
            self.value = value;
            self.status = 'resolved'; // 成功态
            self.onResolevedCallbacks.forEach(fn=>fn());
        }
    }
    function reject(reason){
        if(self.status === 'pending'){
            self.reason = reason;
            self.status = 'rejected'; // 失败态
            // 发布
            self.onRejectedCallbacks.forEach(fn =>fn());
        }
    }
    executor(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this;
    if(self.status === 'resolved'){
        onFulfilled(self.value);
    }
    if(self.status === 'rejected'){
        onRejected(self.reason);
    }
    if(self.status === 'pending'){
        // 订阅
        self.onResolevedCallbacks.push(function(){
            onFulfilled(self.value);
        });
        self.onRejectedCallbacks.push(function(){
            onRejected(self.reason);
        });
    }
}
module.exports = Promise;

使用结果如下:

let Promise = require('./promise');
let p = new Promise(function(resolve,reject){
    setTimeout(function(){
        if(Math.random()>.5){
            resolve("成功");
        }else{
            reject("失败");
        }
    },1000)
});
p.then((value)=>{
    console.log('success',value);
},(reason)=>{
    console.log('error',reason);
})
p.then((value)=>{
    console.log('success',value);
},(reason)=>{
    console.log('error',reason);
})
console.log('end');
//end
//success 成功
//success 成功

Promise高阶版

promise实例是支持链式调用的

在了解promise链式调用之前,我们先来看一下,未使用Promise的,通过回调函数方式实现的异步解决方案:

let fs  = require('fs');
fs.readFile('./name.txt','utf8',function(err,data){
    fs.readFile(data,'utf8',function(err,data){
        console.log(data);
    })
});

很明显这样的嵌套很容易形成回调地狱,也很不简洁。

然后对比一下使用了Promise之后的链式调用:

function readFile(url){
    return new Promise((resolve,reject)=>{
        fs.readFile(url,'utf8',function(err,data){
            if(err) reject(err);
            resolve(data);
        })
    })
}
readFile('./name.txt').then((data)=>{
    return readFile(data); // age.txt1
}).then(data=>{
    return 100;
}).then(data=>{
    console.log(data)
}).catch(err=>{
    throw err
}).then((data)=>{
    console.log(data);
},()=>{
    console.log('error');
})

很明显,我们现在可以看出Promise的一个明显的优点: 就是【链式调用】

Promise链式调用的特点:
1) 如果一个then方法返回一个普通值, 这个值会传递给下一次then中作为成功的结果
2) 不是普通值 (promise 或者报错了),会根据返回的promise是成功还是失败决定下一个then是成功还是失败
3) 捕获错误机制(默认会找离自己最近的then的失败)找不到就向下找
4) promise调用then后 会返回一个新的promise

现在我们继续完善,来实现满足链式调用的Promise类

function Promise (executor){
    let self = this;
    self.value = undefined;
    self.reason = undefined
    self.status = 'pending';
    self.onResolevedCallbacks = [];
    self.onRejectedCallbacks = []; // 存放所有失败的回调
    function resolve(value){
        if(value instanceof Promise){
            // if(value.then && typeof value.then === 'function'){
            return value.then((data)=>{
                resolve(data)
            },y=>{
                reject(y);
            });
            // }
        }
        if(self.status === 'pending'){
            self.value = value;
            self.status = 'resolved'; // 成功态
            self.onResolevedCallbacks.forEach(fn=>fn());
        }
    }
    function reject(reason){
        if(self.status === 'pending'){
            self.reason = reason;
            self.status = 'rejected'; // 失败态
            // 发布
            self.onRejectedCallbacks.forEach(fn =>fn());
        }
    }
    try{
        executor(resolve,reject); // 用户会调用resolve / reject
    }catch(e){
        reject(e); // 说明失败了
    }

}
function resolvePromise(promise2,x,resolve,reject){ // 判断x 是不是promise
    if(promise2 === x){ // 表示防止自己等待自己
        return reject(new TypeError('循环引用了'));
    }
    // 保证当前x 是一个引用类型
    let called; // 表示当前有没有被调用过
    if((x!==null && typeof x === 'object') || typeof x === 'function'){
        // 很有可能是一个promise
        try{
            let then = x.then; // then属性具有getter 此时获取时会发生异常
            if(typeof then === 'function'){ // 就认为promise
                then.call(x,y=>{ // y有可能是一个promise
                    // 一直解析 直到结果是一个常量为止
                    if(called) return;  // 给别人的promise增加的
                    called = true;
                    resolvePromise(promise2,y,resolve,reject);
                    //resolve(y); // 成功拿到成功的结果让promise2变成成功态
                },r=>{
                    if(called) return;
                    called = true;
                    reject(r);
                });
            }else{ // 当前这个then是一个对象 普通对象
                resolve(x); // {a:1}
            }
        }catch(e){ //  防治别人的库中既调用了成功又有失败
            if(called) return;
            called = true;
            reject(e);
        }
    }else{
        resolve(x);// 普通值 直接成功即可
    }
}
Promise.prototype.then = function(onFulfilled,onRejected){
    onFulfilled = typeof onFulfilled === 'function'?onFulfilled : value=> value;
    onRejected = typeof onRejected === 'function'? onRejected:function(err){
        throw err;
    }
    let self = this;
    // 调用then后需要再次 返回一个全新的promise
    // 我需要拿到当前then方法 成功或失败执行后的结果
    let promise2 = new Promise(function(resolve,reject){
        if(self.status === 'resolved'){
            setTimeout(()=>{ // 这里要使用promise2 所有 需要增异步保证可以获取到promise2
                try{
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2,x,resolve,reject);
                }catch(e){
                    reject(e); // 如果执行函数时抛出失败 那么会走向下一个then的失败状态
                }
            },0)
        }
        if(self.status === 'rejected'){
            setTimeout(()=>{
                try{
                    let x = onRejected(self.reason);
                    resolvePromise(promise2,x,resolve,reject);
                }catch(e){
                    console.log('err')
                    reject(e);
                }
            },0)
        }
        if(self.status === 'pending'){
            // 订阅
            self.onResolevedCallbacks.push(function(){
                setTimeout(()=>{
                    try{
                        let x =  onFulfilled(self.value);
                        resolvePromise(promise2,x,resolve,reject);
                    }catch(e){
                        reject(e);
                    }
                },0)
            });
            self.onRejectedCallbacks.push(function(){
                setTimeout(()=>{
                    try{
                        let x =  onRejected(self.reason);
                        resolvePromise(promise2,x,resolve,reject);
                    }catch(e){
                        reject(e);
                    }
                },0);
            });
        }
    });
    return promise2;
}
module.exports = Promise;

Promise最终版

最后我们再补上promise一些其他函数的实现

// 让你实现一个promise的延迟对象 defer
Promise.defer =Promise.deferred = function(){
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
Promise.prototype.catch = function(errFn){
    return this.then(null,errFn)
}
Promise.all = function(values){ console.log('my-promise')
    return new Promise((resolve,reject)=>{
        let arr = []; // 最终结果的数组
        let index = 0;
        function processData(key,value){
            index++;
            arr[key] = value;
            if(index === values.length){ // 如果最终的结果的个数和values的个数相等 抛出结果即可
                resolve(arr);
            }
        }
        for(let i = 0;i<values.length;i++){
            let current = values[i];
            if(current && current.then && typeof current.then == 'function'){
                // promise
                current.then(y=>{
                    processData(i,y);
                },reject)
            }else{
                processData(i,current);
            }
        }
    })
}
Promise.race = function(values){
    return new Promise((resolve,reject)=>{
        for(let i = 0;i<values.length;i++){
            let current = values[i];
            if(current && current.then && typeof current.then == 'function'){
                // race方法 如果已经成功了 就不会失败了 反之一样
                current.then(resolve,reject)
            }else{
                resolve(current);
            }
        }
    });
}
Promise.resolve = function(arg){
    if(arg && arg.then && typeof arg.then == 'function') {
        if(arg instanceof Promise){
            return arg;
        }else{
            return new Promise(arg.then).then();
        }
    }
    return new Promise((resolve,reject)=>{
        resolve(arg);
    })
}
Promise.reject = function(arg){
    return new Promise((resolve,reject)=>{
        reject(arg);
    })
}