Promise原理分析及源码实现

为什么用Promise?

  1. Promise能解决回调地狱问题
  2. Promise能多个异步操作并行,获取最终结果

Promise三种状态

  • Pending Promise对象实例创建时候的初始状态
  • Fulfilled 可以理解为成功的状态
  • Rejected 可以理解为失败的状态

Promise状态转换

image

Promise一旦从pending状态到resolve状态或者reject状态就不会再改变,能保证结果的正确性,不会出现从成功到失败或者从失败到成功的情况。

原生Promise使用分析

let promise = new Promise((resolve,reject)=>{
    resolve("xxx")
    reject("sssss")
  })
  
  promise.then(res=>{
    console.log("success",res);
  },e=>{
    console.log("fail",e);
  })

Promise

Promise

创建一个Promise需要传递一个executor回调函数作为参数,来初始化Promise。executor中有两个参数,一个resolve用来回调返回值或者另一个Promise,一个reject用来回调失败值或者Error。

then()

then函数

then函数需传递两个函数作为参数,一个回调resolve的结果,一个回调reject的结果

const STATUS = {
    "PENDING": "pending",
    "FULFILLED": "fulfilled",
    "REJECTED": "rejected"
}


class Promise {

    /**
     *  Creates a new Promise.
     *
     *  @param executor — A callback used to initialize the promise. 
     *  This callback is passed two arguments: a resolve callback used to resolve the promise
     *  with a value or the result of another promise, and a reject callback used to reject the 
     *  promise with a provided reason or error.
     */
    constructor(executor) {
        this.status = STATUS.PENDING;
        this.value = undefined;
        this.reason = undefined;

        const resolve = (value) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.FULFILLED;
                this.value = value;
            }
        };

        const reject = (reason) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.REJECTED;
                this.reason = reason;
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {

        }
    }
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled — The callback to execute when the Promise is resolved.
     * @param onrejected — The callback to execute when the Promise is rejected.
     * @returns — A Promise for the completion of which ever callback is executed. 
     */
    then(onfulfilled,onrejected) {
        if(this.status === STATUS.FULFILLED){
            onfulfilled(this.value)
        }
        if(this.status === STATUS.REJECTED){
            onrejected(this.reason)
        }
    }
}

module.exports = Promise;
let promise = new Promise((resolve,reject)=>{
    resolve("xxx")
    reject("sssss")
  })
  
  promise.then(res=>{
    console.log("success",res);
  },e=>{
    console.log("fail",e);
  })

//success xxx

现在我们实现了一个简单的Promise,可以在Promise中通过resolve函数传递普通值到then函数中,并在then的onfullfilled中取出,或者通过reject传递在onrejected中取出。并且实现Promise状态从pendding到resolve或reject,不可逆。

实现链式调用

then(onfulfilled,onrejected) {
        let promise2 = new Promise((resolve,reject)=>{
            if(this.status === STATUS.FULFILLED){
                let x = onfulfilled(this.value)
                resolve(x)
            }
            if(this.status === STATUS.REJECTED){
                let x = onrejected(this.reason)
                reject(x)
            } 
            if(this.status === STATUS.PENDING){
                this.onResolvedCallbacks.push(()=>{
                    onfulfilled(this.value);
                });
                this.onrejectedCallbacks.push(()=>{
                    onrejected(this.reason);
                });
            }
        });
        return promise2;
        
    }
    
const resolve = (value) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.FULFILLED;
                this.value = value;
                this.onResolvedCallbacks.forEach(fn=>fn());
            }
        };
const reject = (reason) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn=>fn());
            }
        };
        
let promise = new Promise((resolve,reject)=>{
    resolve("xxx")
    reject("sssss")
  })
  
  promise.then(res=>{
    console.log("success",res);
    return "next"
  },e=>{
    console.log("fail",e);
  }).then(res=>{
    console.log("success1",res)
  })
  //success xxx
  //success1 next

处理返回值是Promise的情况 resolvePromise函数的实现

//解析x类型,决定promise2走成功还是失败
function resolvePromise(promise2, x, resolve, reject) {
    console.log("x", x)
    if (x === promise2) {
        return reject(new TypeError("返回值错误,不能使用同一个promise"))
    }
    //只有object或者function才能是Promise
    if ((typeof x === 'object' && typeof x != null) || typeof x === 'function') {
        let called = false;//成功或者失败方法只能调用一次,防止其他Promise实现错误
        try {
            let then = x.then;//通过判断x上是否有then函数,判断x是否是一个Promise
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return;
                    called = true;
                    // resolve(y) y有可能还是promise 递归解析 直到是普通值为止
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    if (called) return;
                    called = true;
                    reject(r);
                })
            } else {
                reject(x);
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(error);
        }

    } else {
        reject(x);
    }
}

引入Promise A+ 单元测试

Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}

npm install promises-aplus-tests
promises-aplus-tests myPromise.js
单元测试

如果单元测试通过,证明我们的Promise符合Promise A+规范

完整代码

const STATUS = {
    "PENDING": "pending",
    "FULFILLED": "fulfilled",
    "REJECTED": "rejected"
}
//解析x类型,决定promise2走成功还是失败
function resolvePromise(promise2, x, resolve, reject) {
    if (x == promise2) {
        return reject(new TypeError("返回值错误,不能使用同一个promise"))
    }
    //只有object或者function才能是Promise
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        let called = false;//成功或者失败方法只能调用一次,防止其他Promise实现错误
        try {
            let then = x.then;//通过判断x上是否有then函数,判断x是否是一个Promise
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return;
                    called = true;
                    // resolve(y) y有可能还是promise 递归解析 直到是普通值为止
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    if (called) return;
                    called = true;
                    reject(r);
                })
            } else {
                resolve(x);
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(error);
        }

    } else {
        resolve(x);
    }
}

class Promise {

    /**
     *  Creates a new Promise.
     *
     *  @param executor — A callback used to initialize the promise. 
     *  This callback is passed two arguments: a resolve callback used to resolve the promise
     *  with a value or the result of another promise, and a reject callback used to reject the 
     *  promise with a provided reason or error.
     */
    constructor(executor) {
        this.status = STATUS.PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        const resolve = (value) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.FULFILLED;
                this.value = value;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        };

        const reject = (reason) => {
            if (this.status === STATUS.PENDING) {
                this.status = STATUS.REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error)
        }
    }
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled — The callback to execute when the Promise is resolved.
     * @param onrejected — The callback to execute when the Promise is rejected.
     * @returns — A Promise for the completion of which ever callback is executed. 
     */
    then(onfulfilled, onrejected) {
        //判断then传递的参数是否是函数,如果不是则包装成一个函数
        onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : x => x;
        onrejected = typeof onrejected === 'function' ? onrejected : error => {
            throw error
        };
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === STATUS.FULFILLED) {
                setTimeout(() => { //创建一个宏任务,保证promise2已完成创建
                    try {
                        let x = onfulfilled(this.value); //x的返回值有可能是一个新的Promise,需要进行判断和处理
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error) //如果返回异常,捕获并作为失败结果返回
                    }
                    // resolve(x)
                }, 0);

            }
            if (this.status === STATUS.REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onrejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error)
                    }
                    // reject(x)
                }, 0);
            }

            if (this.status === STATUS.PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onfulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)

                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onrejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error)
                        }
                    }, 0);
                });
            }
        });
        return promise2;

    }
    catch(errFn) {
        return this.then(null, errFn);
    }
}

Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
module.exports = Promise;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,612评论 5 471
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,345评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,625评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,022评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,974评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,227评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,688评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,358评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,490评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,402评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,446评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,721评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,802评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,013评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,504评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,080评论 2 341

推荐阅读更多精彩内容