Skip to content

手写promise系列

更为详细的实现过程请查看我的github地址 手写promise系列

ts实现符合实现符合Promises/A+

js
const enum STATUS{  //存放所需要的状态
    pending = 'PENDING',
    fulfilled = 'FULFILLED',
    rejected = 'REJECTED'
}
//核心的逻辑 解析x的类型,决定promise2走成功还是失败
function resolvePromise(promise2,x,resolve,reject){
    // 判断x的值 和promise2的关系  可能是第三方的promise 可能第三方的promise会出现问题
    // console.log(promise2,x,resolve,reject)
    // If promise and x refer to the same object, reject promise with a TypeError as the reason.
    // (如果x和promise2指向同一个对象,则抛错)
    if(x==promise2){
        return reject(new TypeError('出错了'))  //下一个then抛出错误
    }
    if((typeof x==='object' && x!=null) || typeof x==='function'){
        //只有x是对象或者函数才可能是promise
        // console.log(x)
        let called = false;//表示没调用过成功和失败
        try{
            let then = x.then;//取x上的then方法
            if(typeof then =='function'){   //这x.then是当前的then链式的下个接收
                // 如果then是函数,则把x作为this,第一个参数resolvePromise和第二个参数rejectPromise,其中:
                // resolvePromise参数y,rejectPromise参数r,r作为reason
                then.call(x,y=>{
                    if(called) return; //如果已经成功就不能再调失败
                    called = true;
                    //y可能是一个promise,递归解析y,直到y是一个普通值为止
                    resolvePromise(promise2,y,resolve,reject)
                    // resolve(y);//y是x成功返回的结果
                },r=>{
                    if(called) return;
                    called = true;
                    reject(r)
                })
            }else{
                resolve(x);//x是普通函数或者对象
            }
        }catch(e){
            //x.then中抛出的异常的结果e,就以e作为promise失败的reason
            // console.log(e)
            if(called) return;
            called = true;
            reject(e)//走失败逻辑
        }
    }else{
        //如果不是promise则是一个普通值
        resolve(x)
    }
}
 class Promise{
     static deferred;
     status:STATUS;
     value:any;
     reason:any;
     onResolvedCallbacks:Function[];
     onRejectedCallbacks:Function[];
    constructor(executor:(resolve:(value?:any)=>void,reject:(reason?:any)=>void)=>void){
        this.status = STATUS.pending; //当前默认状态
        this.value = undefined;//成功原因
        this.reason = undefined;//失败原因
        this.onResolvedCallbacks = [];//成功回调的函数集合
        this.onRejectedCallbacks = [];//失败回调的函数集合
        const resolve = (value?:any)=>{
            if(this.status==STATUS.pending){
                this.status = STATUS.fulfilled;
                this.value = value;
                //发布模式
                this.onResolvedCallbacks.forEach(fn=>fn());
            }
        }
        const reject = (reason?:any)=>{
            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是否传了,如果类型是一个函数,就不做操作,如果不是函数,则返回一个参数为val的函数,val为this.value
        onFulfilled = typeof onFulfilled =='function'?onFulfilled : val=>val;
        onRejected = typeof onRejected =='function'?onRejected : err => { throw err }
        //每次调用then都产生一个全新的promise
        let promise2 = new Promise((resolve,reject)=>{
            if(this.status == STATUS.fulfilled){
                setTimeout(() => {
                    try{
                        //是个常量的时候
                        let x = onFulfilled(this.value);
                        // console.log(promise2)
                        resolvePromise(promise2,x,resolve,reject)
                        // resolve(x)  //用then的返回值,作为下一次成功结果
                    }catch(e){
                        // 抛错的时候
                        console.log(e)
                        reject(e)
                    }
                }, 0);
            }
            if(this.status == STATUS.rejected){
                setTimeout(() => {
                    try{
                        let x = onRejected(this.reason)
                        resolvePromise(promise2,x,resolve,reject)
                        // resolve(x)  //用then的返回值,作为下一次成功结果
                    }catch(e){
                         // 抛错的时候
                         reject(e)
                    }
                }, 0);
            }
            //如果当前是等待状态,则先把成功回调和失败的回调暂存起来,等状态不是pending的时候调用
            if(this.status == STATUS.pending){
                this.onResolvedCallbacks.push(()=>{
                      //可以增加额外的逻辑
                      setTimeout(() => {
                        try{
                            let x =  onFulfilled(this.value)  //订阅模式
                            resolvePromise(promise2,x,resolve,reject)
                            // resolve(x)  //用then的返回值,作为下一次成功结果
                        }catch(e){
                             // 抛错的时候
                             reject(e)
                        }
                      },0)


                })
                this.onRejectedCallbacks.push(()=>{
                     //可以增加额外的逻辑
                     setTimeout(() => {
                        try{
                            let x = onRejected(this.reason)
                            resolvePromise(promise2,x,resolve,reject)
                            // resolve(x)  //用then的返回值,作为下一次成功结果
                        }catch(e){
                            // 抛错的时候
                            reject(e)
                        }
                     })


                })
            }
        })
        return promise2
    }
 }
 //---------测试是否符合Promise/A+规范
Promise.deferred = function () {
    let dfd = {} as any;
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
 export default Promise

js手写简易版promise

js
const STATUS = {
    PENDING: 'PENDING',
    FUFILLED: 'FUFILLED',
    REJECTED: 'REJECTED'
}

function resolvePromise(x, promise2, resolve, reject) {
    if (promise2 == x) {
        return reject(new TypeError('出错了'))
    }
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        let called = false
        try {
            let then = x.then
            if (typeof then == 'function') {
                then.call(
                    x,
                    function (y) {
                        if (called) return
                        called = true
                        resolvePromise(y, promise2, resolve, reject)
                    },
                    function (r) {
                        if (called) return
                        called = true
                        reject(r)
                    }
                )
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true
            reject(e)
        }
    } else {
        resolve(x)
    }
}
class Promise {
    constructor(executor) {
        this.status = STATUS.PENDING
        this.value = undefined
        this.reason = undefined
        this.onResolvedCallbacks = []
        this.onRejectedCallbacks = []
        // js代码 他看的并不是看声明在哪个位置,而是看执行时此代码是否已经执行了
        const resolve = (val) => {
            if (val instanceof Promise) {
                // 递归解析resolve中的promise
                return val.then(resolve, reject)
            }

            if (this.status == STATUS.PENDING) {
                this.status = STATUS.FUFILLED
                this.value = val
                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 = typeof onFulfilled === 'function' ? onFulfilled : (x) => x
        onRejected =
            typeof onRejected === 'function'
                ? onRejected
                : (err) => {
                      throw err
                  }
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === STATUS.FUFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(x, promise2, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            }
            if (this.status === STATUS.REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(x, promise2, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            }
            if (this.status === STATUS.PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value)
                            resolvePromise(x, promise2, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            resolvePromise(x, promise2, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })
        return promise2
    }
    catch(err) {
        return this.then(null, err)
    }

    static resolve(val) {
        return new Promise((resolve, reject) => {
            resolve(val)
        })
    }
    static reject(reason) {
        return new Promise((resolve, reject) => {
            reject(reason)
        })
    }
}
Promise.deferred = function () {
    let dfd = {}
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}
module.exports = Promise

一.Promise.all原理

js
// 判断是否是promise
const isPromise = (value) => {
    if (
        (typeof value === 'object' && value !== null) ||
        typeof value === 'function'
    ) {
        return typeof value.then === 'function'
    }
    return false
}
Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let arr = []
        let i = 0
        let processData = (index, data) => {
            arr[index] = data // 保证返回结果顺序
            if (++i === promises.length) {
                resolve(arr)
            }
        }
        for (let i = 0; i < promises.length; i++) {
            let current = promises[i]
            if (isPromise(current)) {
                current.then((data) => {
                    processData(i, data)
                }, reject)
            } else {
                processData(i, current)
            }
        }
    })
}

Promise.all可以解决异步并发问题,并且返回的结果按照调用的顺序进行存储。全部成功后才成功否则执行失败逻辑

二.Promise.race原理

1.实现原理

js
Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        // 谁返回的结果最快 就用谁的
        for (let i = 0; i < promises.length; i++) {
            let current = promises[i]
            if (isPromise(current)) {
                current.then(resolve, reject)
            } else {
                resolve(current)
            }
        }
    })
}

race只采用第一个成功或者失败的结果

2.应用场景

js
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 3000)
})
function wrap(p) {
    let abort
    let p1 = new Promise((resolve, reject) => {
        abort = reject
    })
    let newPromise = Promise.race([p1, p])
    newPromise.abort = abort
    return newPromise
}
let p1 = wrap(p)
p1.then(
    (data) => {
        console.log('success', data)
    },
    (err) => {
        console.log('error', err)
    }
)
setTimeout(() => {
    p1.abort('超过2s了')
}, 2000)

借助race的特点,可以实现立即中断promise变为失败态。常用作超时操作

我们可以快速的将node的api方法转化成promise,核心原理就是借助了error-first的特性。在内部手动处理错误

三.手写Promise.finally

finally() 方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数

1、实现原理

js
Promise.prototype.finally = function (callback) {
    return this.then(
        (data) => {
            //等待promise执行完毕 // 等待callback执行完毕之后
            // console.log(Promise.resolve(callback()))
            return Promise.resolve(callback()).then(() => data)
        },
        (err) => {
            // console.log(Promise.resolve(callback()).then(()=>{console.log(err)}))
            return Promise.resolve(callback()).then(() => {
                throw err
            })
        }
    )
}

2、使用

总结: 1)如果callback里面抛出错误,则then(()=>xxx)只传了then一个回调函数(onFulfilled),并没有失败的回调函数,(如果有失败的回调函数,则会在这块捕获错误,)所以会被下个catch接收

2)不管调finally之前是成功还是失败,如果callback里面return 一个普通值,或者return一个成功状态的promise,则走then(()=>throw err)抛出reason给下个catch,或者走then(()=>data),这样就会返回前面的'ok'

四.手写Promise.allSettled

js
//
Promise.allSettled = function (values) {
    function isPromise(x) {
        if ((typeof x === 'object' && x != null) || typeof x === 'function') {
            if (typeof x.then == 'function') {
                return true
            }
        }
        return false
    }
    return new Promise((resolve, reject) => {
        let arr = [] //收集传入的项运行结果
        let times = 0 //调用的次数和传入的参数个数一致的时候,resolve
        function collectResult(val, key, obj) {
            arr[key] = obj
            //注意这里不能用arr.length计数,因为先成功的会是不是promise的项,这个例子中先成功的是0,0成功之后,arr的length已经为3,就会直接resolve
            if (++times === values.length) {
                resolve(arr)
            }
        }
        for (let i = 0; i < values.length; i++) {
            let value = values[i]
            if (value && isPromise(value)) {
                value.then(
                    (y) => {
                        //y是promise返回的值
                        //y i
                        let obj = {
                            status: 'fulfilled',
                            value: y
                        }
                        // console.log(y)
                        collectResult(y, i, obj)
                    },
                    (err) => {
                        let obj = {
                            status: 'rejected',
                            reason: err
                        }
                        collectResult(err, i, obj)
                    }
                )
            } else {
                //value i
                let obj = {
                    status: 'fulfilled',
                    value: value
                }
                collectResult(value, i, obj)
            }
        }
    })
}

promise相关面试题

js
// 相关面试题
/* 
1、怎么让一个node的api => promise的api
  自己实现node的promises
*/
function promisify(fn) {
    return function (...args) {
        //等价于read
        return new Promise((resolve, reject) => {
            fn(...args, (err, data) => {
                if (err) reject(err)
                resolve(data)
            })
        })
    }
}
let read = promisify(fs.readFile)
read('./name.txt', 'utf8').then((data) => {
    console.log(data)
})

//2、判断一个值是不是promise
function isPromise() {
    if ((typeof x === 'object' && x != null) || typeof x === 'function') {
        let then = x.then //取x上的then方法
        if (typeof then == 'function') {
            return true
        }
    }
    return false
}

//3、js代码 他看的并不是看声明在哪个位置,而是看执行时此代码是否已经执行了

function b() {
    console.log(a)
}
let a = 2
b()
//2

完整的实现过程请见 ts手写完整的promise

Released under the MIT License.