一个例子 - 看尽并手写Promise

写了好几天,终于写完了一个符合Promise A+ 规范的promise了,并且跑通了测试用例,改天再仔细讲解吧....

一. 为什么

首先为什么要自己手动实现一个promise呢,因为在使用的过程中,你会发现网上各种文章在分析怎么用,各种奇淫技巧给你讲解promise的写法,一不小心你就落入了敌人的陷进,刚刚背下了这种写法,下一个更奇怪的写法让你怀疑人生,好吧,我怕了,自己实现一个还不行吗,当然网上也有实现的代码,但是还是得自己一行一行敲,这种真实感才会让你记忆尤新

二. 准备工作

  1. Promise/A+规范: 首先可以看到一篇文章叫做Promise/A+规范官方文档, 这可谓是官方文档了,当时这只告诉你规范,怎么写代码是你的事,只要表现一样,能通过所有测试用例则行

  2. 什么是Promsie: 当然首先要看下Promise是什么 promise就是改善js回调地狱的一种代码结构,这种通过链式调用的方法一定程度上看着要好看些,也便于维护,当然最新的async await 同样也事解决这个问题的,但是都是基于promise的原理,所以学这个很有必要

  3. 怎么用Promise: 下面代码可以看到就是promise的简单用法,把一个setTimeout包裹成可以链式调用的Promise,就不用像之前那样写回调函数了,只需要then then then... 所以,then , 我们来写一个promise吧

/**
 *  setTimeout改成promise写法
 */

let Promise = require("./promise")
let p1 = new Promise(function (reslove, reject) {
    setTimeout(function () {
        let num = Math.random();
        console.log(num);
        if (num > 0.5) {
            reslove("成功");
        } else {
            reject("失败")
        }
    }, 2000)
});

p1.then(function (data) {
    console.log("1 resovle",data)
    return data;
}, function (error) {
    console.log("1 reject",error)
}).then(function(data) {
    console.log("2 resolve",data)
},function(error){
    console.log("2 reject",error)
})

三. Promise原理思路

这里写源码的方法就是对照这Promise/A+规范,按照规范一点一点的写,当然不出来可以参照人家写的源码,下面附上手写的基本思路,具体源码在下面

  1. 规范 2.1.Promise States : 告诉我们一个promise有三种状态 pending, fulfiiledrejected三种状态,通俗理解就是等待完成失败, 等待态可以转换为后者,反之不行,这样就定义了三种状态如下
// 定义状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
  1. 创建Promise:上面可以看到,new Promise(function (reslove, reject)在new的时候传入了一个函数委托给promise, 这个函数会被马上调用,同时会再次把内部的resolvereject函数传出来,委托你来调用,当你觉得成功时调用resolve,失败时调用reject,所以我们在promsie内部就要写两个函数resolvereject,那么这两个函数内部干嘛呢,简单的说resolve就是调用你在then里面传入的第一个函数, reject就是调用你在then里面传入的第二个函数,由于可以写很多then,所以内部必须由一个数组来保存,然后依次遍历调用即可,这样你从resolve(data)reject(error)传入的参数就可以被传递到then传入两个函数的回调中,这就是promise实现的基本原理:利用函数当作参数传递和委托调用来简化回调方式为链式调用方式

  2. 规范 2.2.The then Method : 告诉我们new 一个promise 返回的对象有 then方法,所以我们在promise的原型上面增加then方法,里面传入两个函数,在成功调用resolve会被执行前者,失败调用reject执行后者 promise.then(onFulfilled, onRejected),那么then 内部干嘛呢,没错,把传入的两个函数保存到内部的数组里面呀,等到resolve和reject调用的时候确保可以循环调用,你就会问了,万一在then之前就被resolve和reject了怎么办,都没有保存,没错,当你不知道怎么办的时候就用setTimeout把resolve和reject内部代码包一下,放到后面延时执行,所谓事件循环宏任务队列,在当前执行栈执行完才会执行,好吧,这样就能确保then肯定先执行了,我们一个简单的promie就写好了

Promise.prototype.then = function (onFulfilled, onRejected) {
    //... ... 省略细节,可以参考下面源码
}
  1. 规范2.3.The Promise Resolution Procedure: 当然上面实现的只是一个简单的promise, 注意我们then完了还要返回一个新的promise呀,好吧,所以在then中的返回值都要用new 一个新的promise这样包起来返回,更恶心的是,规范允许你在then的参数函数onFulfilledonRejected中还继续返回promise怎么办, 我的老天, 这些个promise也要递归上面所有操作,所以要写一个函数来处理这一切,这就是function resolvePromise(promise2, x, resolve, reject) ,作用就是,判断是返回一个promise则递归调用resolvePromise来解析promise,直到最后不是promise则才调用resolve来完成此层promise,期间任何错误都调用reject的传递失败态

  2. 好了,理解上面基本就理解了整个promise的原理,但是你看下面代码依然很吃力,因为牵扯函数参数,循环递归,状态切换,延时执行等,慢慢熟悉相信会更好的理解的,当你真正理解了,那么你的js水平肯定会更上一层楼,Jason zeng (* . *) 写这篇文章的目的也达到了呀,下面附上源码吧,哈哈!

四. 手写的Promise源码

/**
 * 今天的目标就是写一个符合PromiseA+规范的promise
 * 
 */

// 定义状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function Promise(executor) {
    let self = this;// 先缓存当前promise的实例
    self.status = PENDING; // 设置状态
    self.value = undefined; // fulfilled状态时 返回的信息
    self.reason = undefined; // rejected状态时 拒绝的原因
    self.onResolvedCallbacks = []; // 定义存放成功的回调的数组
    self.onRejectedCallbacks = []; // 定义存放失败的回调的数组

    // 成功时候执行的回调
    function resolve(value) {
        if (value instanceof Promise) {
            return value.then(resolve, reject);
        }
        setTimeout(() => {
            if (self.status === PENDING) {
                self.status = FULFILLED;
                self.value = value;
                self.onResolvedCallbacks.forEach(cb => cb(self.value));
            }
        });
    }

    function reject(reason) {

        setTimeout(() => {
            if (self.status === PENDING) {
                self.status = REJECTED;
                self.reason = reason;
                self.onRejectedCallbacks.forEach(cb => cb(self.reason));
            }
        });
    }

    // 捕获在excutor执行器中抛出的异常
    // new Promise((resolve, reject) => {
    //     throw new Error('error in excutor')
    // })
    try {
        // 因为函数执行会出现异常,所以捕获异常并调用reject转为失败态
        executor(resolve, reject);
    } catch (e) {
        // 用error来reject这个promise
        reject(e);
    }
}

/**
 * resolve中的值几种情况:
 * 1.普通值
 * 2.promise对象
 * 3.thenable对象/函数
 */

/**
 * 对resolve 进行改造增强 针对resolve中不同值情况 进行处理
 * @param  {promise} promise2 promise1.then方法返回的新的promise对象
 * @param  {[type]} x         promise1中onFulfilled的返回值
 * @param  {[type]} resolve   promise2的resolve方法
 * @param  {[type]} reject    promise2的reject方法
 */
function resolvePromise(promise2, x, resolve, reject) {
    // 如果从onFulfilled中返回的x 就是promise2 就会导致循环引用报错
    if (promise2 === x) {
        return reject(new TypeError("循环引用"));
    }

    let called = false; // 避免多次调用
    // 如果x是一个promise对象 (该判断和下面 判断是不是thenable对象重复 所以可有可无)
    if (x instanceof Promise) { // 获得它的终值 继续resolve
        if (x.status === PENDING) { // 如果为等待态需等待直至 x 被执行或拒绝 并解析y值
            x.then(y => {
                resolvePromise(promise2, y, resolve, reject);
            }, reason => {
                reject(reason);
            });
        } else { // 如果 x 已经处于执行态/拒绝态(值已经被解析为普通值),用相同的值执行传递下去 promise
            x.then(resolve, reject);
        }
        // 如果 x 为对象或者函数
    } else if (x !== null && ((typeof x === "object") || (typeof x === "function"))) {
        try { // 是否是thenable对象,具有then方法的对象或函数
            let then = x.then;
            if (typeof then === "function") { // 如果还有then则调用then
                then.call(x, function (y) {
                    if (called) return;
                    called = true;
                    // 递归调用到最后为一个没有then的普通对象           
                    resolvePromise(promise2, y, resolve, reject);
                }, function (reason) {
                    if (called) return;
                    called = true;
                    reject(reason);
                })
            } else { // 说明是一个普通对象/函数, 则调用resolve来解决它
                resolve(x);
            }
        } catch (e) {
            if (called) return;
            called = true;
            reject(e);
        }
    } else { // 如果返回不是thenable对象,直接解决它!
        resolve(x);
    }
}

/**
 * [注册fulfilled状态/rejected状态对应的回调函数]
 * @param  {function} onFulfilled fulfilled状态时 执行的函数
 * @param  {function} onRejected  rejected状态时 执行的函数
 * @return {function} newPromsie  返回一个新的promise对象
 */
Promise.prototype.then = function (onFulfilled, onRejected) {
    // 如果没有传参数,表示这个then没有逻辑,把结果往后抛,注意reject就是把错误继续throw
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
    onRejected = typeof onRejected === "function" ? onRejected : reason => { throw reason };
    let self = this;

    let promise2;
    // ------------- executor内部是同步的时候,直接调用
    // 如果当前promise的状态是成功态,onFulfilled直接取值
    if (self.status === FULFILLED) {
        return promise2 = new Promise(function (resolve, reject) {
            setTimeout(() => {
                try {
                    let x = onFulfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });

        })
    }
    // 失败态,直接取值reject
    if (self.status === REJECTED) {
        return promise2 = new Promise(function (resolve, reject) {
            setTimeout(() => {
                try {
                    let x = onRejected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });
        })
    }


    if (self.status === PENDING) { //等待态
        return promise2 = new Promise(function (resolve, reject) {
            self.onResolvedCallbacks.push(function (value) {
                try {
                    let x = onFulfilled(value);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });
            self.onRejectedCallbacks.push(function (reason) {
                try {
                    let x = onRejected(reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });
        });
    }
};

/**
 * [Promise.all Promise进行并行处理]
 * @param  {Array} promises promise对象组成的数组作为参数
 * @return {function} 返回一个Promise实例
 */
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        // let done = gen(promises.lenght, resolve);
        let count = 0;
        let values = []; // 由于promise.all([]) 这里传递的是数组,所以这里保留空数组
        promises.forEach((promise, index) => {
            promise.then(value => { // 调用then表示当他们完成后会传递到这里来
                //done(value,index);
                values[index] = value;  // 保存所有完成的值
                if (++count === promises.length) { // 当所有都完成的时候再解决它们
                    resolve(values);
                }
            }, reason => {
                reject(reason);
            })
        })
    });
};


/**
 * [Promise.race Promise比赛,完成则继续走后面的]
 * @param  {Array} promises promise对象组成的数组作为参数
 * @return {function} 返回一个Promise实例
 */
Promise.race = function (promises) {
    return new Promise((reslove, reject) => {
        promises.forEach((promise, index) => {
            // 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理(取决于哪一个更快)
            // 这里只是简单的传递,都给它们用then先绑定好成功或失败的函数,谁先成功或失败就会执行谁,这里就不用管了
            promise.then(reslove, reject);
        })
    });
}


// 用于promise方法链时 捕获前面onFulfilled/onRejected抛出的异常
Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected);
}

// 用promise包装下,等待自动调用
Promise.resolve = function (value) {
    return new Promise(resolve => {
        resolve(value);
    });
}

Promise.reject = function (reason) {
    return new Promise((resolve, reject) => {
        reject(reason);
    });
}

/**
 * 基于Promise实现Deferred的
 * Deferred和Promise的关系
 * - Deferred 拥有 Promise
 * - Deferred 具备对 Promise的状态进行操作的特权方法(resolve reject)
 *
 *参考jQuery.Deferred
 *url: http://api.jquery.com/category/deferred-object/
 */
Promise.deferred = function () { // 延迟对象
    let defer = {};
    defer.promise = new Promise((resolve, reject) => {
        defer.resolve = resolve;
        defer.reject = reject;
    });
    return defer;
}

try {
    module.exports = Promise
} catch (e) {
}

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

推荐阅读更多精彩内容