Promise使用以及源码推理

285 阅读5分钟

Promise是什么

Promise是高版本浏览器自带的一个类,不兼容低版本浏览器。Promise对象有三个状态,等待态pending,成功态resolved,失败态rejected,默认为pending,成功或者失败后不可再变化状态。

我们为什么用Promise

在异步编程中可以帮助我们解决回调地狱的痛苦。方便有连带关系的异步请求编写,也可实现并发多个异步请求,同步异步的请求结果,还可以统一处理错误。

Promise如何使用

调用Promise时,传入一个excutor方法,其中包括两个参数,resolve、reject,excutor方法内部代码是同步的。每个Promise对象都有一个then方法,then接收两个函数,第一个是成功后执行的方法,第二个是失败后执行的方法,这两个方法都是异步的。前面说到excutor是同步的,那么下面代码结果是什么?为什么?

// promise 最基本用法
new Promise((resolve,reject)=>{
    console.log(1);
    resolve();
    console.log(2);
}).then(()=>{
    console.log('成功');
}, ()=>{
    console.log('失败');
});
console.log(3);
// promise excutor中也可以使用异步 比如进行ajax封装等
new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve();
    }, 0);
}).then(()=>{
    console.log('成功');
}, ()=>{
    console.log('失败');
});
// 方便的链式调用 后一个请求依赖前一个请求
let fs = require('fs');
function read(fileName){
    return new Promise((resolve, reject)=>{
    fs.readFile(fileName, 'utf8', (err, data)=>{
        if (err) reject(err);
        resolve(data);
    })
  });
}

// 逐个处理错误
// read('./1.txt').then((data)=>{
//   return read(data);
// },(err)=>{
//   console.log(1, err);
// }).then((data)=>{
// console.log('success', data);
// },(err)=>{
// console.log(2, err);
// });

// catch统一处理错误
read('./1.txt').then((data)=>{
    return read(data);
}).then((data)=>{
    console.log('success', data);
}).catch((err)=>{
    console.log(err); 
});

如果返回的的是Promise对象,那么此次的执行结果(成功或者失败),就会传递到下一个then方法中。如果是一个普通值或者undefined,就会走下一个then的成功。如果抛出异常就会走下一个then的失败,如果then什么也不传,直接传到下一个then的成功。那么看以下代码?结果是什么?

read('./1.txt').then((data)=>{
    throw new Error('err');
}).then((data)=>{
    console.log('age', data);
}).then().then(null, (err)=>{
    // 直接穿透到后面then 执行第二个方法 这里捕获了错误 不会走后面的catch了
    console.log('then', err); 
}).catch((err)=>{
    console.log('catch', err);
}).then((data)=>{
    console.log(data);
});
new Promise((resolve, reject)=>{
  resolve(1);
}).then((x)=>{
  return x+1;
}).then((x)=>{
  throw new Error(x);
}).catch(()=>{
  return 1
}).then((x)=>{
  return x+1
}).then((x)=>{
  console.log(x);
}).catch((err)=>{
    console.error(err);
})
new Promise((resolve,reject)=>{
  resolve(1);
}).then((res)=>{
  console.log(res);
  return 2;
}).catch((err)=>3)
.then((res)=>{
  console.log(res);
});

Promise处理并发 all方法返回的是一个promise对象

// Promise.all([1, 2, 3]).then((data)=>{
Promise.all([read('./1.txt'), read('./2.txt')]).then((data)=>{
    // data是一个数组 按照请求时的顺序已排好
    console.log(data);
}).catch((err)=>{
    // 如果有一个失败 就会走catch
    console.log(err);
});

自己实现一个Promise类

参考https://promisesaplus.com/,promiseA+规范。

// 基本版 Promise
class Promise{
    constructor(excutor){
        // status 为状态 默认pending 成功为fulfilled 失败为rejected
        this.status = 'pending';
        // 保存成功后的值
        this.resolve = undefined;
        // 保存失败的原因
        this.reject = undefined;
        let resolve = (value)=>{
            // 只有pengding的时候才能修改状态
            if(this.status === 'pending'){
                this.status = 'fulfilled';
                this.resolve = value;
            }
        };
        let reject = (reason)=>{
            // 只有pengding的时候才能修改状态
            if(this.status === 'pending'){
                this.status = 'rejected';
                this.reject = reason;
            }
        };
        // 如果excutor执行的时候出错了
        try{
            excutor(resolve, reject);
        }catch(e){
            reject(e);
        }
    }
    then(onFulfilled, onReject){
        if(this.status === 'fulfilled'){
            onFulfilled(this.resolve);
        }
        if(this.status === 'rejected'){
            onReject(this.reject);
        }
    }
}

let p = new Promise((resolve, reject)=>{
    resolve(1);
});
p.then((value)=>{
    console.log('v', value)
}, (err)=>{
    console.log('err', err);
});
// 解决excutor中有异步代码 以及一个promise对象调用多个then方法的问题 
class Promise{
    constructor(excutor){
        this.status = 'pending';
        this.resolve = undefined;
        this.reject = undefined;
        this.onResolveCbs = [];
        this.onRejectedCbs = [];
        let resolve = (value)=>{
            if(this.status == 'pending'){
                this.status = 'fulfilled';
                this.resolve = value;
                // 成功 执行数组里的所有方法
                this.onResolveCbs.forEach((item)=>item());
            }
        };
        let reject = (reason)=>{
            if(this.status == 'pending'){
                this.status = 'rejected';
                this.reject = reason;
                this.onRejectedCbs.forEach((item)=>item());
            }
        };
        try{
            excutor(resolve, reject);
        }catch(e){

        }
    }
    then(onFulfilled, onReject){
        // excutor 中有异步 把回调存起来 可能出现多个
        if(this.status == 'pending'){
            this.onResolveCbs.push(()=>{
                onFulfilled(this.resolve);
            });
            this.onRejectedCbs.push(()=>{
                onReject(this.reason);
            });
        }
        if(this.status == 'fulfilled'){
            onFulfilled(this.resolve);
        }
        if(this.status == 'rejected'){
            onReject(this.reject);
        }
    }
}

let p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve(1);
    }, 1000);
});
p.then((value)=>{
    console.log('v', value)
}, (err)=>{
    console.log(err);
});
p.then((value)=>{
    console.log('v', value)
}, (err)=>{
    console.log(err);
});
// 解决链式调用
class Promise{
    constructor(excutor){
        this.status = 'pending';
        this.resolve = undefined;
        this.reject = undefined;
        this.onResolveCbs = [];
        this.onRejectedCbs = [];
        let resolve = (value)=>{
            if(this.status == 'pending'){
                this.status = 'fulfilled';
                this.resolve = value;
                this.onResolveCbs.forEach((item)=>item());
            }
        };
        let reject = (reason)=>{
            if(this.status == 'pending'){
                this.status = 'rejected';
                this.reject = reason;
                this.onRejectedCbs.forEach((item)=>item());
            }
        };
        // 如果excutor执行的时候出错
        try{
            excutor(resolve, reject);
        }catch(e){
            reject(e);
        }
    }
    then(onFulfilled, onReject){
        // 链式调用
        let promise2 = new Promise((resolve, reject)=>{
            // excutor 中有异步 把回调存起来
            if(this.status === 'pending'){
                this.onResolveCbs.push(()=>{
                    // 加定时器 不然promise2为undefined 
                    // try catch防止执行onFulfilled时出现异常
                    setTimeout(()=>{
                        try{
                            let x = onFulfilled(this.resolve);
                            resolvePromise(promise2, x, resolve, reject);
                        }catch(e){
                            reject(e);
                        }
                    }, 0);
                });
                this.onRejectedCbs.push(()=>{
                    setTimeout(()=>{
                        try{
                            let x = onReject(this.reason);
                            resolvePromise(promise2, x, resolve, reject);                 
                        }catch(e){
                            reject(e);
                        }
                    }, 0);
                });
            }else if(this.status === 'fulfilled'){
                setTimeout(()=>{
                    try{
                        let x = onFulfilled(this.resolve);
                        resolvePromise(promise2, x, resolve, reject);
                    }catch(e){
                        reject(e);
                    }
                }, 0);
            }else if(this.status === 'rejected'){
                setTimeout(()=>{
                    try{
                        let x = onReject(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    }catch(e){
                        reject(e);
                    }
                }, 0);
            }
        });
        return promise2;
    }
    catch(onRejected){
        return this.then(null, onRejected);
    }
    finally(cb){
        return this.then((data)=>{
            cb();
            return data;
        }, (err)=>{
            cb();
            throw err;
        });
    }
    static race(promises){
        return Promise((resolve, reject)=>{
            for(let i = 0 ; i < promises.length; i++){
                let promise = promises[i];
                if(typeof promise.then == 'function'){
                    promise.then(resolve, reject);
                }else{
                    resolve(promise);
                }
            }
        });
    }
    static all(promises){
        return new Promise((resolve, reject)=>{
            let arr = [],
                i = 0;
            let processData = (index, data)=>{
                arr[index] = data;
                if(++index == promises.length){
                    resolve(arr);
                }
            };
            for(let i = 0; i < promises.length; i++){
                let promise = promises[i];
                if(typeof promise.then == 'function'){
                    promise.then((data)=>{
                        processData(i, data);
                    }, reject);
                }else{
                    processData(i, promise);
                }
            }
        });
    }
    static resolve(data){
        return new Promise((resolve, reject)=>{
            resolve(data);
        });
    }
    static reject(err){
        return new Promise((resolve, reject)=>{
            reject(err);
        });
    }
    static deferred(){
        let dfd = {};
        dfd.promise = new Promise((resolve, reject)=>{
            dfd.resolve = resolve;
            dfd.reject = reject;
        });
        return dfd;
    }
    static defer(){
        let dfd = {};
        dfd.promise = new Promise((resolve, reject)=>{
            dfd.resolve = resolve;
            dfd.reject = reject;
        });
        return dfd;
    }
}
function resolvePromise(promise2, x, resolve, reject){
    if(promise2 == x)throw new TypeError('不能重复引用');
    let called = false;
    if(x && typeof x == 'object' || typeof x == 'function'){
        try{
            let then = x.then;
            if(typeof then == 'function'){
                then.call(x, (y)=>{
                    if(called)return;
                    called = true;
                    resolvePromise(x, y, resolve, reject);
                }, (err)=>{
                    if(called)return;
                    called = true;
                    reject(err);
                });
            }else{
                resolve(x);
            }
        }catch(e){
            if(called)return;
            called = true;
            reject(e);
        }
    }else{
        resolve(x);
    }
}

let p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve(1);
    }, 1000)
});
p.then((value)=>{
    // throw Error(value);  // 这里抛出异常或者返回普通值 都没有问题
    return value;
}, (err)=>{
    console.log('1', err);
}).then((value)=>{
    console.log('v', value)
}, (err)=>{
    console.log('2', err);
});