震惊,实现Promise竟然只需要一分钟,史上最简单的Promise源码教学

一、概念

  • Fulfilled-Promise,被完成的Promise对象
  • Rejected-Promise,被拒绝的Promise对象

二、Promise的特点

2.1 then/catch/finally都会返回一个新的Promise对象

// 测试then
let p1 = Promise.resolve();
let p2 = p1.then();
p2.finally(()=>{
    console.log(p1 === p2); //打印false,说明是新对象
});
// 测试catch
let p1 = Promise.reject(1);
let p2 = p1.catch();
p2.finally(()=>{
    console.log(p1 === p2); //打印false,说明是新对象
});
// 测试finally
let p1 = Promise.resolve();
let p2 = p1.finally();
p2.finally(()=>{
    console.log(p1 === p2); //打印false,说明是新对象
});

2.2 即使then/catch内部函数返回Promise对象,then/catch仍然是返回新对象,与内部函数返回的Promise有相同的执行结果

// 测试then
let onFulfilledRes = null;
let thenReturn = Promise.resolve()
    .then(() => {
        onFulfilledRes = Promise.resolve()
        return onFulfilledRes;
    });
thenReturn.then((v) => {
    console.log(thenReturn === onFulfilledRes); //打印false,说明是新对象
});
// 测试catch
let onRejectedRes = null;
let thenReturn = Promise.reject()
    .catch(() => {
        onRejectedRes = Promise.resolve()
        return onRejectedRes;
    });
thenReturn.then((v) => {
    console.log(thenReturn === onRejectedRes);  //打印false,说明是新对象
});
// 测试新对象的执行结果
Promise.resolve()
    .then(()=>Promise.resolve(1)) 
    .then((v)=>console.log(v)) //打印1,说明新对象和内部函数有相同的执行结果

2.3 如果onFulfilled/onRejcted返回值是Rejected-Promise,则then/catch也将会返回新的Rejected-Promise对象

Promise.resolve()
    .then(()=>Promise.reject(1))
    .catch((v)=>console.log(v)) // 调用了catch,打印1

2.4 finally无法修改Promise状态

// 
Promise.resolve(1)
    .finally(() => 2)
    .then((v) => {
        console.log(v)  // 打印1
    });

2.5 可以多次调用then/catch/finally方法

// 测试then和finally,catch是一样的
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    }, 1000);
});
p.then((v) => console.log(v)); //打印1
p.then((v) => console.log(v)); //打印1
p.finally(() => console.log("finally1")); //打印finally1
p.finally(() => console.log("finally2")); //打印finally2

2.6 promise按事件队列进行执行

Promise.resolve().then(() => {
    console.log("1-1");
}).then(() => {
    console.log("1-2")
});
Promise.resolve().then(()=>{
    console.log("2-1");
    return Promise.resolve();
}).then(()=>{
    console.log("2-2")
});
Promise.resolve().then(()=>{
    console.log("3-1");
}).then(()=>{
    console.log("3-2")
});
// 打印结果
1-1
2-1
3-1
1-2
3-2
2-2

三、实现难点

3.1 构建事件队列

  • 队列规则:先进先出
  • 每次触发执行then/catch/finally内部函数时插入到事件队列中
  • 循环执行队列处理
// 简易的事件队列
const queue = [];
function runQueue() {
    while (queue.length > 0) {
        let func = queue.shift();
        func && func();
    }
    setTimeout(runQueue,10)
}

3.2 then/catch/finally内部函数的处理

  • 内部函数处理时,需要返回一个新的Promise对象

    function handleThen(promise, onFulfill, onRejected) {
        return new MyPromise((resolve, reject) => {
            promise._resolveWatcher.push(createWatcher(promise, resolve, reject, onFulfill));
            promise._rejectedWatcher.push(createWatcher(promise, resolve, reject, onRejected));
            promise._tryFinish();
        });
    }
    
  • 内部函数执行时,需要通过事件队列

    function createWatcher(promise, resolve, reject, innerFunc) {
        return () => {
            eventQueue.addQueue(() => {
                handleInnerFuncRes(resolve, reject, innerFunc, promise);
            });
        };
    }
    

    eventQueue 是上面封装的事件队列

  • 内部函数执行结果,如果返回Promise的处理

    function handleInnerFuncRes(resolve, reject, innerFunc, promise) {
        let res = innerFunc && innerFunc(promise._result);
        if (res && res instanceof MyPromise) {
            res.then((value) => {
                resolve(value);
            }, (reason) => {
                reject(reason);
            });
        } else {
            resolve(res);
        }
    }
    
  • finally内部函数不需要监听返回值,直接使用父级的返回值即可。

    this.finally = function (callback) {
        return this.then((v) => {
            callback && callback();
            return v;
        }, (reason) => {
            callback && callback();
            return MyPromise.reject(reason);
        });
    };
    

    通过then实现

源码:https://github.com/joey-lucky/MyPromise

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

推荐阅读更多精彩内容