实现一个符合 Promise/A+ 规范的 MyPromise

Promise

实现一个符合 Promise/A+ 规范的 MyPromise,并实现 resolve、reject、all、race、defer、deferred等静态方法。

MyPromise

  1. 作用:创建 MyPromise实例。Promise

  2. MyPromise接收一个回掉函数 executor

  3. MyPromise状态

    • pending

      • 可以转换成 fulfilled 或 rejected
    • fulfilled

      • 不可改变成其他状态
    • rejected

      • 不可改变成其他状态
  4. onFulfilledCallbacksonRejectedCallbacks

    • 两个数组,数组每一项是一个函数。分别接收then里面的第一个参数和第二个参数。

    • 状态是 pending 的回掉函数。

  5. resolve

    • promise的状态是fulfilled异常是的处理函数

    • 接收 value 参数

      • 如果是promise,执行then

      • 如果不是promise,把value做为参数传给onFulfilledCallbacks里的每个函数。

  6. reject

    • promise的状态是rejected异常是的处理函数

    • 接收 reason 参数,把reason做为参数传给onRejectedCallbacks里的每个函数。

  7. 执行 executor,如果有异常,抛给reject

  8. 因为Promise是在同步代码执行完成后再执行,所以要把Mypromise的执行方法resolvereject放在异步队列里


function MyPromise(executor) {

    if (typeof executor !== 'function') {

        throw new TypeError('Promise resolver ' + executor + ' is not a function');

    }

    let self = this;

    this.status = 'pending';

    this.value = undefined;

    this.reason = undefined;

    this.onFulfilledCallbacks = [];

    this.onRejectedCallbacks = [];

    function resolve(value) {
        if (value instanceof MyPromise) {
            return value.then(resolve, reject);
        }
        if (self.status === 'pending') {            
            self.value = value;
            self.status = 'fulfilled';
            self.onFulfilledCallbacks.forEach(item => item(value));
        }
    }
    
    function reject(reason) {
        if (self.status === 'pending') {
            self.reason = reason;
            self.status = 'rejected';
            self.onRejectedCallbacks.forEach(item => item(reason));
        }
    }


    try {

        executor(resolve, reject);

    } catch (e) {

        reject(e);

    }

}

MyPromise.prototype.then

  1. 作用:接收两个函数参数,第一个函数的参数是 resolve传入的参数,第二个参数是 reject传入的参数。Promise#then

  2. onFulfilled

    • MyPromise 成功时执行的方法

    • resolve 的参数会作为value传给 onFulfilled

  3. onRejected

    • MyPromise 失败时执行的方法

    • reject 的参数会作为value传给 onRejected

  4. 返回一个 MyPromise 实例 newPromise,方便链式调用

  5. 对三种状态分别处理

    • 每个状态中创建 newPromise

    • fulfilled

      • 直接执行 onFulfilled,返回值x

      • newPromisex以及newPromise里的resolvereject做为参数传给 resolutionPromise

      • 把 MyPromise 的参数放在异步队列里

    • rejected

      • 直接执行 onRejected,返回值x

      • newPromisex以及newPromise里的resolvereject做为参数传给 resolutionPromise

      • 把 MyPromise 的参数放在异步队列里

    • pending

      • 状态待定,把fulfilledrejected里的异步函数分别加到 onFulfilledCallbacksonRejectedCallbacks的最后一位
  6. resolutionPromise 后面细说

  7. catch捕获异常,执行 reject


MyPromise.prototype.then = function (onFulfilled, onRejected) {

    let self = this;

    typeof onFulfilled !== 'function' && (onFulfilled = function (value) {

        return value;

    });

    typeof onRejected !== 'function' && (onRejected = function (reason) {

        throw reason;

    });

    let newPromise;

    /**

    *  分别处理实例的三种状态

    */

    if (self.status === 'fulfilled') {

        newPromise = new MyPromise(function (resolve, reject) {

            setTimeout(function () {

                try {

                    let x = onFulfilled(self.value);

                    resolutionPromise(newPromise, x, resolve, reject);

                } catch (e) {

                    reject(e);

                }

            });

        });

    }

    if (self.status === 'rejected') {

        newPromise = new MyPromise(function (resolve, reject) {

            setTimeout(function () {

                try {

                    let x = onRejected(self.reason);

                    resolutionPromise(newPromise, x, resolve, reject);

                } catch (e) {

                    reject(e);

                }

            });

        });

    }

    if (self.status === 'pending') {

        newPromise = new MyPromise(function (resolve, reject) {

            self.onFulfilledCallbacks.push(function (value) {

                setTimeout(function () {

                    try {

                        let x = onFulfilled(value);

                        resolutionPromise(newPromise, x, resolve, reject);

                    } catch (e) {

                        reject(e);

                    }

                });

            });

            self.onRejectedCallbacks.push(function (reason) {

                setTimeout(function () {

                    try {

                        let x = onRejected(reason);

                        resolutionPromise(newPromise, x, resolve, reject);

                    } catch (e) {

                        reject(e);

                    }

                });

            });

        });

    }

    return newPromise;

};

MyPromise.prototype.catch

  1. 作用:捕获异常

  2. 返回 MyPromise


MyPromise.prototype.catch = function (onRejected) {

    return this.then(undefined, onRejected);

};

The Promise Resolution Procedure

  1. Promise解析过程,是以一个 promise、一个值 xresolve, reject做为参数的抽象过程

  2. promise 等于 xreject 抛出异常 new TypeError('循环引用')

  3. x如果不是对象(不包括 null)或者函数,执行 resolve(x)

  4. 获取 x.then 赋值给 then

    • then 如果是 function

      • x做为 this 调用then,第一个参数是 resolvePromise,第二个参数是 rejectPromise

      • resolvePromiserejectPromise只有第一次调用有效

      • resolvePromise参数为 y,执行 resolutionPromise(promise, y, resolve, reject)

      • rejectPromise参数为 r,执行 reject(r)

    • then 如果不是 function

      • 执行 resolve(x)
  5. 用捕获上一步的异常

    • 执行 reject(e)

    • 如果执行过 resolvePromiserejectPromise,忽略


function resolutionPromise(promise, x, resolve, reject) {

    if (promise === x) {

        reject(new TypeError('循环引用'));

    }

    let then, called;

    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {

        try {

            then = x.then;

            if (typeof then === 'function') {

                then.call(x, function (y) {

                    if (called)

                        return;

                    called = true;

                    resolutionPromise(promise, y, resolve, reject);

                }, function (r) {

                    if (called)

                        return;

                    called = true;

                    reject(r);

                })

            } else {

                resolve(x);

            }

        } catch (e) {

            if (called)

                return;

            reject(e);

        }

    } else {

        resolve(x);

    }

}

MyPromise 静态方法

MyPromise.all


MyPromise.all = function (promises) {

    let called = false;

    return new MyPromise(function (resolve, reject) {

        let newArr = [], count = 0;

        for (let i = 0; i < promises.length; i++) {

            let item = promises[i];

            if (!(item instanceof MyPromise)) {

                item = MyPromise.resolve(item);

            }

            item.then(function (data) {

                if (!called) {

                    newArr[i] = data;

                    if (i == count) {

                        resolve(newArr);

                        count++;

                    }

                }

            }, function (e) {

                if (!called) {

                    reject(e);

                    called = true;

                }

            });

        }

    });

};

MyPromise.race


MyPromise.race = function (promises) {

    return new MyPromise(function (resolve, reject) {

        let called = false;

        for (let i = 0; i < promises.length; i++) {

            let item = promises[i];

            if (!(item instanceof MyPromise)) {

                item = MyPromise.resolve(item);

            }

            item.then(function (data) {

                if (!called) {

                    resolve(data);

                    called = true;

                }

            }, function (e) {

                if (!called) {

                    reject(e);

                    called = true;

                }

            });

        }

    })

};

MyPromise.resolve


MyPromise.resolve = function (value) {

    if (value instanceof MyPromise) {

        return value;

    }

    return new MyPromise(function (resolve, reject) {

        if (typeof value !== null && typeof value === 'object' && typeof value.then === 'function') {

            value.then();

        } else {

            resolve(value);

        }

    })

};

MyPromise.reject


MyPromise.reject = function (e) {

    return new MyPromise(function (resolve, reject) {

        reject(e);

    })

};

test

  • npm i -g promises-aplus-tests

  • promises-aplus-tests Promise.js

源码

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