码迷,mamicode.com
首页 > 其他好文 > 详细

Promise A+ 简单实现

时间:2021-04-20 14:26:37      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:imp   each   ret   可变   用法   yun   http   The   测试方法   

写一个符合 Promise A+ 规范的 Promise

类型定义

// MyPromise.ts
type resType = (value?: any) => void;
type rejType = (reason?: any) => void;
type executorType = (res: resType, rej?: rejType) => void;
type onfulfilledType = (value?: any) => any;
type onrejectedType = (reason?: any) => any;

/**
 * `promise` 的三个状态  
 * 分别有 `pending`, `fulfilled`, `rejected`
 */
enum stat {
    PENDING,
    FUIFILLED,
    REJECTED,
}

interface MyPromise {
    value: any, 
    reason: any,
    status: stat,
    onfulfilledCallbacks: onfulfilledType[],
    onrejectedCallbacks: onrejectedType[],
}

模拟 then 的微任务

要完成 Promise 的功能其实不一定要微任务,只要是异步执行的就可以

不过我这里用的是 node.js 特供 process.nextTick(),属于微任务

运行 promises-aplus-tests 进行测试最后会显示运行时长,可以对比一下与 setTimeout() 的差距

// MyPromise.ts
function asyncExecute(cb: () => void): void {
    // setTimeout(cb) 也可以
    process.nextTick(cb);
}

MyPromise 类

主要工作是写三个方法,整体思路就是发布-订阅模式

then 负责订阅状态的变化,注册相应事件;而 resolve/reject 负责更改状态,发布这一事件

resolvePromise 用于处理 onfulfilled/onrejected 的返回值

// MyPromise.ts
class MyPromise implements MyPromise {
    constructor(executor: executorType) {
	 // code ...
    }

    private static resolvePromise(bridgePromise: MyPromise, target: any, resolve: resType, reject: rejType): void {
        // code ...
    }

    then(onfulfilled?: any, onrejected?: any): MyPromise {
        // code ...
    }
}

MyPromise 构造函数

resolve()/reject() 想象成 emit() 会好理解些

constructor(executor: executorType) {
    this.value = undefined; // fulfilled 状态 返回的值
    this.reason = undefined; // rejected 状态 拒绝的原因
    this.status = stat.PENDING; // 初始状态
    this.onfulfilledCallbacks = []; // 存储 onfulfilled 回调
    this.onrejectedCallbacks = []; // 存储 onrejected 回调

    const reject: rejType = reason => {
        if (this.status !== stat.PENDING) return;
        asyncExecute(() => {
            this.status = stat.REJECTED;
            this.reason = reason;
            this.onrejectedCallbacks.forEach(cb => cb(this.reason));
        });
    }

    const resolve: resType = value => {
        if (this.status !== stat.PENDING) return;
        asyncExecute(() => {
            this.status = stat.FUIFILLED;
            this.value = value;
            this.onfulfilledCallbacks.forEach(cb => cb(this.value));
        });
    }

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

MyPromise.prototype.then()

想象成 EventEmitteron() ,订阅实例的 onfulfilled/onrejeced 事件,以bridgePromiseresolve()/reject() 作为其回调

/**
 * 注册fulfilled状态/rejected状态对应的回调函数,本质上是个发布-订阅模式
 * @param onfulfilled fulfilled状态时 执行的函数
 * @param onrejected rejected状态时 执行的函数
 * @returns 返回新的MyPromise
 */
then(onfulfilled?: any, onrejected?: any): MyPromise {
	// 返回值穿透
    onfulfilled = typeof onfulfilled === "function" ? onfulfilled : (value: any) => value; 
	// 错误冒泡的关键
    onrejected = typeof onrejected === "function" ? onrejected : (reason: any) => { throw reason };

    const bridgePromise = new MyPromise((resolve, reject) => {
        // pending: 订阅当前实例的状态变更
        if (this.status === stat.PENDING) {
            this.onfulfilledCallbacks.push(value => {
                try {
                    const x: any = onfulfilled(value);
                    MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            });

            this.onrejectedCallbacks.push(reason => {
                try {
                    const x: any = onrejected(reason);
                    MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            });

        // fulfilled: 变更 bridgePromise 状态为 fulfilled
        // 如果 onfulfilled 不是函数就会将 this.value 传给 x, 即返回值穿透的原理
        } else if (this.status === stat.FUIFILLED) {
            asyncExecute(() => {
                try {
                    const x: any = onfulfilled(this.value);
                    MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            });

        // rejected: 变更 bridgePromise 状态为 rejected 
        // 一般来说直接 throw reason, 被捕获异常走 reject 这条路, 即错误冒泡的原理
        } else {
            asyncExecute(() => {
                try {
                    const x: any = onrejected(this.reason);
                    MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            });
        }
    });
    
    // 链式调用的关键
    return bridgePromise;
}

处理 onfulfilled/onrejected 的返回值

个人感觉这里是最难的地方,可以看到上面的then 方法中,没有直接以 onfulfilled/onrejected 的返回值作为 bridgePromise的不可变值(指 value/reason) ,而是根据其类型分别进行处理

Promise a+ 对这里的处理有比较多的要求,需要注意的点我都注释了需求的编号,可以到这里查看需求

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

/**
 * 对resolve进行改造 针对resolve中不同值情况分别进行处理
 * @param bridgePromise then将要返回的新的MyPromise对象
 * @param target then中onfulfilled/onrejected的返回值
 * @param resolve bridgePromise的resolve方法
 * @param reject bridgePromise的reject方法
 */
private static resolvePromise(bridgePromise: MyPromise, target: any, resolve: resType, reject: rejType): void {
    // promise aplus 2.3.1
    if (target === bridgePromise) {
        return reject(new TypeError("循环引用"));
    }

    // promise aplus 2.3.3.3.3 防止多次调用
    let called = false; 

    // promise aplus 2.3.2
    if (target instanceof MyPromise) {
        // promise aplus 2.3.2.1
        if (target.status === stat.PENDING) {
            target.then((x: any) => {
                MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
            }, reject);
        } else { // promise aplus 2.3.2.2 & 2.3.2.3
            target.then(resolve, reject);
        }
    } else if (target && (typeof target === "object" || typeof target === "function")) {
        // promise aplus 2.3.3
        try {
            const then = target.then;
            // promise aplus 2.3.3.3
            if (typeof then === "function") {
                then.call(target, (x: any) => {
                    if (called) return;
                    called = true;
                    MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
                }, (reason: any) => {
                    if (called) return;
                    called = true;
                    reject(reason);
                });
            } else {
                resolve(target);
            }
        } catch (e) { // promise aplus 2.3.3.2
            if (called) return;
            called = true;
            reject(e);
        }
    } else {
        resolve(target);
    }
}

测试

??比较懒,我直接在同一文件下执行这个测试方法

测试工具 githubhttps://github.com/promises-aplus/promises-testsREADME 写了大致用法

// MyPromise.ts
// test
interface defer {
    promise?: MyPromise,
    resolve?: resType,
    reject?: rejType,
}

const adapter = {
    deferred() {
        let def: defer = {};
	    def.promise = new MyPromise((resolve, reject) => {
	        def.resolve = resolve;
	        def.reject = reject;
	    });
	return def;
    }
}

// 下载 promises aplus 测试工具
// github: https://github.com/promises-aplus/promises-tests
// npm install promises-aplus-tests -g

// 直接运行 typescript:
// npm install ts-node -g
// ts-node MyPromise.ts 

var promisesAplusTests = require("promises-aplus-tests");
promisesAplusTests(adapter, console.log);

执行代码:

ts-node MyPromise.ts

技术图片

完成啦,不过还有一些 Promise 的方法没有实现,比如 race/all 等,Promise a+ 没有做要求,就先不在这里介绍了。

可以参考三元的博客,宝藏级前端大佬。

Promise A+ 简单实现

标签:imp   each   ret   可变   用法   yun   http   The   测试方法   

原文地址:https://www.cnblogs.com/hhteng/p/14672477.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!