Promise实现

35 阅读5分钟

基于PromiseA+实现

    const PENDING = 'PENDING'
    const FULFILLED = 'FULFILLED'
    const REJECTED = 'REJECTED'
    
    // 定义解析promise返回值是不是Promise做处理的方法(链式调用实现)
    const resolvePromise(promise2, x, resolve, reject) {
        if (promise2 === x) {
            return reject(new TypeError('[TypeError: 循环引用!]'))
        }
        // 1. 正常情况我们要分析x是不是 promise, 如果x是普通值就直接resolve就行
        // 2. 判断x是不是promise, 不能用instanceof 因为别人的promise和自己的没法instanceof(promise可以和其他人写的互相调用)
        // 3. 只有x 是函数或对象是才有可能是一个promise
        if ((typeof x === 'object' && x !== null) || (typeof x === 'function')) {
            let called = false; // 标记防止别人的promise即调成功, 又调失败, 或者多次调用
            try {
                let then = x.then;
                if (typeof then.call(x) === 'function') {
                    then.call(x, function(y) {
                        if (called) return;
                        // 可能又返回了promise,所以要递归解析直到是普通值为止
                        called = true;
                        resolvePromise(promise2, y, resolve, reject)
                    }, function(r) {
                        if (called) return;
                        called = true;
                        reject(r)
                    })
                } else {
                    resolve(x) // then就是一个对象
                }
            } catch(e) {
                if (called) return;
                called = true;
                reject(e) // 取then的时候抛错了
            }
        } else {
            resolve(x) // 普通值直接结束
        }
    };
    
    class Promise {
        constructor(executor) {
            this.status = PENDING;
            this.value = undefined;
            this.reson = undefined;
            
            // 异步的时候需要把调用存起来,等状态变为非pending时再执行
            this.onResolvedCallbacks = [];
            this.onRejectedCallbacks = [];
            
            // 用户调用的成功和失败
            const resolve = (value) => {
                //  实现直接resolve一个promise的情况 Promise.resolve(new Promise()) // 里面是普通值没有问题,是Promise就需要如下处理
                if (value instanceof Promise) {
                    return value.then(resolve, reject)
                }
                
                // 状态只能改变一次
                if (this.status === PENDING) {
                    this.value = value;
                    this.status = FULFILLED;
                    this.onResolvedCallbacks.forEach(fn => fn());
                }
            }
            const reject = (reson) => {
                if (this.status === PENDING) {
                    this.reson = reson;
                    this.status = REJECTED;
                    this.onRejectedCallbacks.forEach(fn => fn());
                }
            }
            
            try {
                // 立即执行
                executor(resolve, reject)
            } catch(e) {
                reject(e)
            }
        };
        
        then(onFulfilled, onRejected) {
            // 给默认值 如果调then没传成功和失败函数,要给默认值,一直传到有处理的地方 then().then().then(() => {}, () => {})
            onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : v => v;
            onRejected = typeof onRejected == 'function' ? onRejected : err => { throw err };
            
            let promise2 = new Peomise((resolve, reject) => {
                // 同步处理
                if (this.status === FULFILLED) {
                    setTimeout(() => {
                        try {
                            // 根据第一个promise的then返回值决定下一个promise调用成功还是失败
                            let x = onFulfilled(this.value);
                            // 这里需要判断返回值是不是promise
                            // promise2这时候还没创建出来,所以这里的代码要用异步执行包裹
                            resolvePromise(promise2, x, resolve, reject)
                        } cacth(e) {
                            reject(e)
                        }
                    })
                };
                if (this.status === REJECTED) {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reson)
                            resolvePromise(promise2, x, resolve, reject)
                        } cacth(e) {
                            reject(e)
                        }
                    })
                }
                // 异步处理
                if (this.status === PENDING) {
                    // 订阅 push包一层是为了可扩展, 可以拿到函数的返回值
                    this.onResolvedCallback.push(() => {
                        setTimeout(() => {
                            try {
                                let x = onFulfilled(this.value) // 调用的时候就是非pending状态有value了
                                resolvePromise(promise2, x, resolve, reject)
                            } cacth(e) {
                                reject(e)
                            }
                        })
                    });
                    this.onRejectedCallbacks.push(() => {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reson)
                                resolvePromise(promise2, x, resolve, reject)
                            } cacth(e) {
                                reject(e)
                            }
                        })
                    });
                }
            })
        }
        
        // catch 的本质就是then
        catch(errCallback) {
             return this.then(null, errCallback)
         }
         
         // 无论成功还是失败都执行
         finally(fn) {
             return this.then(
                 (data) => Promise.resolve(fn()).then(() => data),
                 (err) =>  Promise.resolve(fn()).then((err) => { throw err })
             )
         }
        
        // Promise.resolve 会等待内部的promise执行完毕
        static resolve(value) {
            return new Promise((resolve, reject) => {
                resolve(value)
            })
        }
        
        // Promise.reject 不会等待内部的promise执行完毕
        static reject(reson) {
            return new Promise((resolve, reject) => {
                reject(reson)
            })
        }
        
        // 全部成功才成功, 有一个失败就失败, 成功结果与调用顺序一致
        static all(values) {
            let ret = [] // 最终的结果
            let len = values.length;
            return new Promise((resolve, reject) => {
                values.forEach((val, idx) => {
                    // 兼容val不是promise的情况也给包装成promise
                    Promise.resolve(val).then(data => {
                        ret[idx] = data;
                        if (--len === 0) resolve(ret)
                    }, reject) //  任何一个promise失败, 直接调用最外层的返回
                })
            })
        }
        
        // 赛跑 谁快用谁的结果 一个成功就成功, 一个失败就失败 使用场景: 处理超时 比如500毫秒之后调某个promise接口的reject就走到失败
        static race(values) {
            return new Promise((resolve, reject) => {
                values.forEach((val, idx) => {
                    Promise.resolve(val).then(resolv, reject)
                })
            })
        }
        
        // 只要其中的一个promise成功,就返回那个已经成功的, 全部都失败就返回一个失败的promise和aggregateError类型的实例
        static any(values) {
            const reasons = []
            return new Promise((resolve, reject) => {
                values.forEach(val => {
                    val.then(resolve, err => {
                        reasons.push(err)
                        if (reasons.length === values.length) {
                            reject(new AggregateError(reasons))
                        }
                    })
                })
            })
        }
        
        // 不会失败, 数组里的promise实例全部改变状态就成功, 成功返回promise实例都改变的数组[{status:’ fullfilled’,value},{status:’ fullfilled’,value},{status:’ fullfilled’,value}]
        static allSettled(values) {
            return new Promise((resolve) => {
                const ret = [];
                let len = values.length;
                values.forEach(val => {
                    val.then(res => {
                        ret.push({ status: FULFILLED, value: res})
                        if (--len === 0) {
                            resolve(results)
                        }
                     }, err => {
                        ret.push({ status: REJECTED, value: err})
                        if (--len === 0) {
                            resolve(ret)
                        }
                    })
                })
            })
        }
    };
    
    module.export = Promise;

简单使用

    const Promise = require('./promise');
    const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('成功')
        }, 1000)
    })
    
    promise.then(value => { // 订阅
        console.log('成功', value)
    }, reson => {
        console.log('失败', reson)
    })
    // 多次调用
    promise.then(value => {
        console.log('成功', value)
    }, reson => {
        console.log('失败', reson)
    })

链式调用使用

    // 读取两个文件内容的异步场景
    const fs = require('fs');
    const path = require('path');
    
    // 异步方法先变成promise
    function readFile(url) {
        return new Promise((resolve, reject) => {
            fs.readFile(path.resolve(__dirname, url), 'utf8', function(err,data) {
                if(err) {
                    return reject(err)
                }
                resolve(data)
            })
        })
    }
    // 1. 如果一个promise.then中的方法 返回的是一个peomise, 那么就会自动解析返回的promise,采用它的状态作为下一次then的结果
    // 2. 如果then中的方法, 返回的不是promise, 则会直接走下一次的then的成功
    // 3. then中的方法抛出异常, 走下一次then的失败
    // 什么时候会让promise.then走失败?
    //        a) 返回失败的promise
    //        b) 抛出异常
    // promise实现链式调用采用的是每次调用then方法都返回一个全新的prmise, 为什么不return this ? (如果一直是同一个promise, promise不能从成功变为失败)
            
    readFile('fileUrl.txt').then((data) => {
        // 又返回一个promise, 成功走下一次成功,失败走下一次失败
        return readFile(data); 
    }, (err) => {
        console.log(err);
        // 这里的失败返回结果只要不是promise就走下一次的成功
        return undefined;
    }).then((data) => {
        console.log('下一次的then success' + data);
        // 如果这里抛错了,则再走下一次then的失败
        // throw new Error('出错了')
    }, (err) => {
        console.log('下一次的then fail' + err)
    }).then((data) => {
        console.log('上一次then成功并且返回promise成功或者失败返回值并且不抛错')
    }, (err) => {
        console.log(err, '上一次then抛错走这里') 
    })
    

promiseify实现

   function promiseify(fn) { // fs.readFile 实现一个文件读取的promiseify 
       return function (...args) {
           return new Promise((resolve, reject) => {
               fn(...args, function(err, data) {
                   if (err) return reject(err);
                   resolve(data)
               })
           })
       }
   }
   
   // 使用 
   const readFile = promiseify(fs.readFile);
   readFile(path.resolve(__dirname, 'note.txt'), 'utf8').then(data => console.log(data))

promiseifyAll实现

    function promiseifyAll(obj) {
        for(let key in obj) {
            if (typeof obj[key] === 'function') {
                obj[key] = promiseify(obj[key])
            }
        }
    }
    // 使用
    promiseifyAll(fs); // 内部会识别对象上的所有方法 将属性依次promise化
    fs.readFile(path.resolve(__dirname, 'note.txt'), 'utf8').then(data => console.log(data))