手写promise源码介绍

1、promise的resolve reject then函数实现

1.1、promise有3种状态

const STATUS = {
    PENDING: 'PENDING',  // 等待中
    FUFILLED: 'FUFILLED',  // 成功
    REJECTED: 'REJECTED' 	// 失败
};

1.2、promise使用的时候需要new Promise()

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        // reject('失败了');
        resolve('成功了');
    }, 1000);
    // throw new Error('错误了');
});

//  订阅起来
p.then(
    (data) => {
        console.log('success', data);
    },
    (reason) => {
        console.log('fail', reason);
    }
);

1.3 由上可知 promise是一个构造函数,参数是一个函数,并且形参是resolve, reject,并且有then方法(then接受2个函数,成功和失败)

class Promise {
    constructor(executor) {
        this.status = STATUS.PENDING;
 		try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    },
    then(onFulfilled, onRejected) {
	}
}

1.4 处理创建promise时的状态, resolve()后会改变status

class Promise {
    constructor(executor) {
        this.status = STATUS.PENDING;
        this.value = undefined;
        this.reason = undefined;
        // 存放成功回调 (订阅列表)
        this.onResolvedCallbacks = [];
        // 存放失败回调
        this.onRejectedCallbacks = [];
        const resolve = (val) => {
            // 是promise 就继续递归执行
            if (val instanceof Promise) {
                return val.then(resolve, reject);
            }
            // 改变为成功状态并执行then里面的成功回调
            if (this.status == STATUS.PENDING) {
                this.status = STATUS.FUFILLED;
                this.value = val;
                // resolve时就调用列表 (发布执行)
                this.onResolvedCallbacks.forEach((fn) => fn());
            }
        };
        // 改变为失败状态并执行then里面的失败回调
        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 (e) {
            reject(e);
        }
    }
}

1.5 then函数链式调用

// promise链式调用
// 1. 如果then方法中 返回不是一个promise,
// 会将这个值传递给外层下次then的成功结果
// 2. 如果执行then方法中的方法出错了 抛出异常 走到下一个then的失败中
// 3. 如果返回的是一个promise, 结果会作为下一次then的成功或者失败
function read(...args) {
    return new Promise((resolve, reject) => {
        fs.readFile(...args, function (err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}
// 1.出错会失败  2.返回的promise会出错
// catch 就是then的别名 没有成功只有失败(找最近的有限处理,处理不了的找下一层)
// then方法为什么可以链式调用, 每次调用then都是返回一个新的promise
read(resolvePath('name.txt'), 'utf8')
    .then(
        (data) => {
            throw new Error('error了');
            // return 100;
        },
        (err) => {
            console.log('1then err', err);
            // throw new Error(err);
        }
    )
    .then(
        (data) => {
            console.log('2then', data);
        }
        // (err) => {
        //     console.log('2then err', err);
        // }
    )
    .then(null, (err) => {
        console.log('catch', err);
        return 2;
    })
    .then((data) => {
        console.log('last then', data);
    });

1.6 链式调用说明一个then函数之后,会返回个新的promise对象,它才有then方法
then返回promise
假设x是一个正常值,resolve一个值就ok 新返回的promise就可以触发then里面的成功回调了

class Promise {
	...
    then(onFulfilled, onRejected) {
        let promise2 = new Promise((resolve, reject) => {
            // 同步处理
            if (this.status === STATUS.FUFILLED) {
              
                    try {
                        let x = onFulfilled(this.value);
                        resolve(x)
                    } catch (error) {
                        reject(error);
                    }
                
            }
            if (this.status === STATUS.REJECTED) {
                
                    try {
                        let x = onRejected(this.reason);
                        resolve(x, promise2, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
            }
            // 异步处理
            if (this.status === STATUS.PENDING) {
                // 装饰模式 切片编程
                this.onResolvedCallbacks.push(() => {
                   
                        try {
                            // todo...
                            let x = onFulfilled(this.value);
                            resolve(x);
                        } catch (error) {
                            reject(error);
                        }
                  
                });
                this.onRejectedCallbacks.push(() => {
                   
                        try {
                            // todo...
                            let x = onRejected(this.reason);
                            resolve(x);
                        } catch (error) {
                            reject(error);
                        }
                 
                });
            }
        });
        return promise2;
    }
}

1.7 假如then的onFulfilled函数return一个新的promise 要如何处理呢 如下图

// 判断返回值和promise的关系
let promise2 = p1.then(
    (data) => {
        return new Promise((resolve, reject) => {
            resolve('999');
        });
    },
    (err) => {
        return 200;
    }
);
promise2.then(
    (data) => {
        console.log('promise2', data);  // 999
    },
    (err) => {
        console.log('err', err);
    }
);

// 或者嵌套promise 递归处理
let promise2 = new Promise((resolve, reject) => {
    let p = new Promise((resolve, reject) => {
        resolve(
            new Promise((resolve, reject) => {
                resolve(
                    new Promise((resolve, reject) => {
                        setTimeout(() => {
                            resolve('999');
                        }, 1000);
                    })
                );
            })
        );
    });
    let a = Promise.resolve(p);
    console.log('a', a);
    p.then((data) => {
        // 999
        console.log(data); 
    });
    resolve(p);
});
promise2.then((data) => {
    // 999
    console.log(data);
});

如何实现呢
看下面
增加一个resolvePromise函数
resolvePromise解析x是不是promise或者普通值,resolve该值就能传给下一个promise2的then函数里面
如果是嵌套promise, 就递归处理 直到拿到普通值 再resolve

// 防止x是一个特殊对象 就报错了
// Object.defineProperties('x', 'then' {
//     get() {
//         if(time == 2) {
//             throw new Error();
//         }
//     }
// })
// 看x是普通值还是promise 如果是promise采用他的状态
function resolvePromise(x, promise2, resolve, reject) {
    // 防止自己等待自己完成
    if (promise2 == x) {
        return reject(new TypeError('出错了'));
    }
    // 看x是普通值还是promise 如果是promise 要采用他的状态
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        // x可以是一个对象或者函数
        let called; // 标志位 防止resolve reject同时进行
        try {
        	// 34行
            let then = x.then;
            // 看这个对象是否有then方法
            if (typeof then == 'function') {
                // 有then函数 我就认为x是一个promise

                // 如果x是promise 那么就采用他的状态
                // 类似这个语法
                // x.then(() => {
                //
                // }, err => {
                //
                // })
                // 复用32行就好了 防止有报错可能性 14行介绍了
                then.call(
                    x,
                    function (y) {
                        // 调用返回的promise 用他的结果 作为下一次then的结果。
                        if (called) return;
                        called = true;
                        // y就是成功的值 999  或者y是一个promise 要用递归思路
                        // 递归解析成功后的值 直到他是一个普通值为止
                        resolvePromise(y, promise2, resolve, reject);
                    },
                    (r) => {
                        if (called) return;
                        called = true;
                        reject(r);
                    }
                );
            } else {
                resolve(x); // 此时x 就是一个普通对象
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(e);
        }
    } else {
        resolve(x); // x不是promise 是一个原始数据
    }
    // 不是promise 直接调用resolve
}

class Promise {
   ...
    then(onFulfilled, onRejected) {

     
        let promise2 = new Promise((resolve, reject) => {
            // 同步处理
            if (this.status === STATUS.FUFILLED) {
                // 创建一个宏任务 为了拿到promise2 (promise2还没new完拿不了的)
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(x, promise2, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            }
            if (this.status === STATUS.REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(x, promise2, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            }
            // 异步处理
            if (this.status === STATUS.PENDING) {
                // 装饰模式 切片编程
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            // todo...
                            let x = onFulfilled(this.value);
                            resolvePromise(x, promise2, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            // todo...
                            let x = onRejected(this.reason);
                            resolvePromise(x, promise2, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
            }
        });
        return promise2;
    }
   
}

1.8 then函数为什么能够等待resolve(x)后执行呢?
1.8.1 如果是同步任务 then之前其实就resolve或者reject了,status状态改变了
then函数直接进去后就是FUFILLED或者REJECTED状态,就可以执行resolvePromise了。

1.8.2 如果是异步任务,then进去后还是pending状态, 故把执行的onFulfilled放进去onResolvedCallbacks队列或者onRejected函数放进去onRejectedCallbacks队列,
等resolve时状态改变后再执行队列 函数

try catch 是为了捕捉onFulfilled函数的错误

const resolve = (val) => {
            // 是promise 就继续递归执行
            if (val instanceof Promise) {
                return val.then(resolve, reject);
            }
            if (this.status == STATUS.PENDING) {
                this.status = STATUS.FUFILLED;
                this.value = val;
                // resolve时就调用列表 (发布执行)
                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());
            }
        };

1.9 如果then是空函数,怎么透传呢
例如下面用法

const p = new Promise((resolve, reject) => {
    resolve('ok');
});
// 功能1-- p.then空传参也可以resolve的值透传下去
p.then()
    .then()
    .then((data) => {
        console.log(data); // ok
    });

如果then是空函数,那就自动补充onfullfilled后者onrejected函数吧

 then(onFulfilled, onRejected) {
        // 没传onFulfilled 就设置默认函数 就可以默认拿到then里面的data了
        onFulfilled =
            typeof onFulfilled === 'function' ? onFulfilled : (data) => data;
        onRejected =
            typeof onRejected === 'function'
                ? onRejected
                : (err) => {
                      throw err;
                  };
         ...

2、catch函数实现

其实catch就是then的换种写法,
代码如下

 catch(err) {
        // 默认直走失败 没有成功 then的简单写法  err是函数
        return this.then(null, err);
    }

3、Promise.resolve如何实现呢

下面的使用 Promise.resolve返回新的promise2

let pp = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(222);
    }, 1000);
});
// Promise.resolve可以等待一个promise执行完毕
Promise.resolve(pp).then((data) => {
    console.log(data);
});

本质就是一个静态方法

   static resolve(val) {
        return new Promise((resolve, reject) => {
            resolve(val);
        });
    }

注意:如果pp是一个promise就会等待pp里面的resolve()之后再处理, val.then函数参数resolve函数会拿到222,参数resolve是外层的resolve, 改变了外层promise的状态 就能后续then了

const resolve = (val) => {
   // 是promise 就继续递归执行 
    if (val instanceof Promise) {
        return val.then(resolve, reject);
    }
    if (this.status == STATUS.PENDING) {
        this.status = STATUS.FUFILLED;
        this.value = val;
        // resolve时就调用列表 (发布执行)
        this.onResolvedCallbacks.forEach((fn) => fn());
    }
};

4、Promise.defer实现

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

使用时减少new Promise使用 代码更省略

function read(...args) {
    //defer减少了 new Promise 套用 应该是模仿$.defer()等
    let dfd = Promise.defer();
    fs.readFile(...args, function (err, data) {
        if (err) return dfd.reject(err);
        dfd.resolve(data);
    });
    return dfd.promise;
}

let p1 = read(resolvePath('name.txt'), 'utf8');

let promise2 = p1.then(
    (data) => {
        return data;
    },
    (err) => {
        return 200;
    }
);

5、 Promise.all()方法实现

使用如下
promise.all会等待全部promise resolve后 执行then

let fs = require('fs').promises;
let getName = fs.readFile(resolvePath('./name.txt'), 'utf8');
let getAge = fs.readFile(resolvePath('./age.txt'), 'utf8');

// Promise.all方法返回一个promise
Promise.all([1, getName, getAge, 2]).then((data) => {
    console.log(data);
});

实现如下
本质返回一个promise
记录每个promise的执行结果, 如果是promise 就用promise.then去获取resolve后的值 再记录如result数组,times 记录次数 等到全部执行完毕就resolve(result)

static all(promises) {
        // 判断是不是promise
        function isPromise(val) {
            return typeof val.then == 'function';
        }

        return new Promise((resolve, reject) => {
            let result = [];
            let times = 0;
            function processData(index, val) {
                result[index] = val;
                // 每次加1做处理次数  等于传入的promises数组长度时 就完成了 resolve就行了
                if (++times === promises.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                let p = promises[i];
                if (isPromise(p)) {
                    p.then((data) => {
                        processData(i, data); // 普通值
                    }, reject); // reject处理后 返回的promise就可以catch了
                } else {
                    processData(i, p); // 普通值
                }
            }
        });
    };

6、 finally()方法实现

finally无论如何都会执行 data会是undefined
使用方法

Promise.reject(123)
    .finally((data) => {
        // 不管成功还是失败 都执行
        // 这里传入的函数 无论如何都会执行
        console.log('finally', data); //undefined
        // finally可以返回一个promise
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('ok'); // Promise.reject(123) resolve会跑下面的失败, 但是用123的值 reject会跑到下面的err 用ok
            }, 2000);
        });
    })
    .then(
        (data) => {
            console.log('suc:', data); 
            return 2;
        },
        (err) => {
            console.log('err:', err);
        }
    );

源码分析:

   finally(callback) {
        return this.then(
            (data) => {
                console.log('then data', data);
                // 让函数执行内部会调用方法 如果方法是promise需求等待她完成
                return Promise.resolve(callback()).then((res) => {
                    return data;
                });
            },
            (err) => {
                // reject
                return Promise.resolve(callback()).then(() => {
                    throw err;
                });
            }
        );
    }

最后附一份完整手写的源码
总结:

其实Promise本质是一个构造函数, 里面实现了各种方法 then catch finally等。
为了达到链式调用目的, 每个方法其实都是返回一个新的promise对象, 那么该对象就能链式继续调用then了, 至于控制异步status状态变化, 其实就是把then里面onfullfilled先挂起到队列, 等resolve后就再调用队列的函数,达到了回调的效果。

/*
 * @description:
 * @author: steve.deng
 * @Date: 2020-10-14 18:12:38
 * @LastEditors: steve.deng
 * @LastEditTime: 2020-10-26 16:48:31
 */
const STATUS = {
    PENDING: 'PENDING',
    FUFILLED: 'FUFILLED',
    REJECTED: 'REJECTED'
};

// 防止x是一个特殊对象 就报错了
// Object.defineProperties('x', 'then' {
//     get() {
//         if(time == 2) {
//             throw new Error();
//         }
//     }
// })
// 看x是普通值还是promise 如果是promise采用他的状态
function resolvePromise(x, promise2, resolve, reject) {
    // 防止自己等待自己完成
    if (promise2 == x) {
        return reject(new TypeError('出错了'));
    }
    // 看x是普通值还是promise 如果是promise 要采用他的状态
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        // x可以是一个对象或者函数
        let called; // 标志位 防止resolve reject同时进行
        try {
            let then = x.then;
            // 看这个对象是否有then方法
            if (typeof then == 'function') {
                // 有then函数 我就认为x是一个promise

                // 如果x是promise 那么就采用他的状态
                // 类似这个语法
                // x.then(() => {
                //
                // }, err => {
                //
                // })
                // 复用32行就好了 防止有报错可能性 14行介绍了
                then.call(
                    x,
                    function (y) {
                        // 调用返回的promise 用他的结果 作为下一次then的结果。
                        if (called) return;
                        called = true;
                        // y就是成功的值 999  或者y是一个promise 要用递归思路
                        // 递归解析成功后的值 直到他是一个普通值为止
                        resolvePromise(y, promise2, resolve, reject);
                    },
                    (r) => {
                        if (called) return;
                        called = true;
                        reject(r);
                    }
                );
            } else {
                resolve(x); // 此时x 就是一个普通对象
            }
        } catch (error) {
            if (called) return;
            called = true;
            reject(e);
        }
    } else {
        resolve(x); // x不是promise 是一个原始数据
    }
    // 不是promise 直接调用resolve
}

class Promise {
    constructor(executor) {
        this.status = STATUS.PENDING;
        this.value = undefined;
        this.reason = undefined;
        // 存放成功回调 (订阅列表)
        this.onResolvedCallbacks = [];
        // 存放失败回调
        this.onRejectedCallbacks = [];
        const resolve = (val) => {
            // 是promise 就继续递归执行
            if (val instanceof Promise) {
                return val.then(resolve, reject);
            }
            if (this.status == STATUS.PENDING) {
                this.status = STATUS.FUFILLED;
                this.value = val;
                // resolve时就调用列表 (发布执行)
                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 (e) {
            reject(e);
        }
    }
    then(onFulfilled, onRejected) {
        // 没传onFulfilled 就设置默认函数 就可以默认拿到then里面的data了
        onFulfilled =
            typeof onFulfilled === 'function' ? onFulfilled : (data) => data;
        onRejected =
            typeof onRejected === 'function'
                ? onRejected
                : (err) => {
                      throw err;
                  };
        let promise2 = new Promise((resolve, reject) => {
            // 同步处理
            if (this.status === STATUS.FUFILLED) {
                // 创建一个宏任务 为了拿到promise2 (promise2还没new完拿不了的)
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        resolvePromise(x, promise2, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            }
            if (this.status === STATUS.REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(x, promise2, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            }
            // 异步处理
            if (this.status === STATUS.PENDING) {
                // 装饰模式 切片编程
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            // todo...
                            let x = onFulfilled(this.value);
                            resolvePromise(x, promise2, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            // todo...
                            let x = onRejected(this.reason);
                            resolvePromise(x, promise2, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
            }
        });
        return promise2;
    }
    catch(err) {
        // 默认直走失败 没有成功 then的简单写法  err是函数
        return this.then(null, err);
    }
    finally(callback) {
        return this.then(
            (data) => {
                console.log('then data', data);
                // 让函数执行内部会调用方法 如果方法是promise需求等待她完成
                return Promise.resolve(callback()).then((res) => {
                    return data;
                });
            },
            (err) => {
                // reject
                return Promise.resolve(callback()).then(() => {
                    throw err;
                });
            }
        );
    }
    static resolve(val) {
        return new Promise((resolve, reject) => {
            resolve(val);
        });
    }
    static reject(reason) {
        return new Promise((resolve, reject) => {
            reject(reason);
        });
    }
    static defer() {
        let dfd = {};
        dfd.promise = new Promise((resolve, reject) => {
            dfd.resolve = resolve;
            dfd.reject = reject;
        });
        return dfd;
    }
    static all(promises) {
        function isPromise(val) {
            return typeof val.then == 'function';
        }

        return new Promise((resolve, reject) => {
            let result = [];
            let times = 0;
            function processData(index, val) {
                result[index] = val;
                // 每次加1做处理次数  等于传入的promises数组长度时 就完成了 resolve就行了
                if (++times === promises.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                let p = promises[i];
                if (isPromise(p)) {
                    p.then((data) => {
                        processData(i, data); // 普通值
                    }, reject); // reject处理后 返回的promise就可以catch了
                } else {
                    processData(i, p); // 普通值
                }
            }
        });
    }
}

// 测试时会调用这个方法
// Promise.defer = Promise.deferred = function () {
//     let dfd = {};
//     dfd.promise = new Promise((resolve, reject) => {
//         dfd.resolve = resolve;
//         dfd.reject = reject;
//     });
//     return dfd;
// };

// 安装测试工具包
// npm install promises-aplus-tests -g
module.exports = Promise;

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值