仿写Promise(详细解释每一步的作用)

Promse基本用法

function  testPrm (){
    let promise = new Promise((resolve, reject) =>{
        let r = parseInt(Math.random()*10)
        if(r%2 ==0){
            resolve("成功")
        }else {
            reject('失败')
        }
    } )
    return promise
}
testPrm().then((res)=>{
     console.log(res)
},(rej)=>{
    console.log(rej)
})

Promse解决的问题

Promise能够有效的解决js异步回调地狱问题

Promse规范

  • promise有三种状态:pending,fulfilled,rejected。
  • pending代表等待的状态,在此状态下,可能执行resolve()的方法,也可能执行reject()方法
  • fulfilld代表成功态,此状态下执行resolve()方法,
  • rejected代表失败态,此状态下执行reject()方法,一旦成功了就不能失败,反过来也是一样
  • 每个promsie都有一个then方法
  • 如果new promise 报错了会走失败态(throw new Error(‘报错’)也会走失败态)
  • 能够链式调用

我们先实现promise的三种状态和成功或者失败后的返回值

class myPromise{
    constructor(executor) {
        this.state ='pending'; //初始化状态
        this.returnVal =undefined; //成功返回值
        this.retReson = undefined; //失败返回值
        // 成功方法
        let resolve = (value) => {
             //将状态修改为成功,并存储当前传递的参数
            if (this.state == 'pending') {
                this.state = 'fullFilled'
                this.value = value
            }
        }
        // 失败方法
        let reject = (reason) => {
            //将状态修改为失败,并存储当前传递的参数
            if (this.state == 'pending') {
                this.state = 'rejected'
                this.reason = reason
            }
        }
        //保证函数执行时不会报错,如果保存直接调用失败方法
        try {
            // 执行函数,外接传递两个函数供其执行
            executor(resolve, reject)
        } catch (err) {
            // 失败则直接执行reject函数
            reject(err)
        }
    }
    /**
    * then 传递两个函数
     * onFullFilled 成功方法
     * onRejected   失败方法
    * */
    then(onFullFilled, onRejected){
        //同步执行 状态为fulfuilled,执行onFullFilled,传入成功的值
        if (this.state == 'fullFilled') {
            onFullFilled(this.value)
        }
        //同步执行 状态为rejected,执行onRejected,传入失败的值
        if (this.state == 'rejected') {
            onRejected(this.reason)
        }
    }
}
const p = new myPromise((resolve, reject) => {
    let r = parseInt(Math.random()*10)
    if(r%2 ==0){
        resolve('success')   // 走了成功就不会走失败了
    }else  {
        throw new Error('失败')
        // 失败了就走resolve throw后下面的不会执行
        reject('failed')       // 走了失败就不会走成功
    }
})
p.then((res) => {
    console.log(res)
}, (err) => {
    console.log("捕获",err)
})
//多次运行可输出成功 或者失败两种不同结果

运行流程

  • new myPromise 传递一个函数,在内部执行,并返回两个函数供外部成功或者失败时调用(class中的resolve,reject ),调用resolve保存了成功传递参数值,reject保存了失败/错误时传递的值。当then去调用时,then传递两个函数(成功和失败函数),根据state值调用对应的函数执行。

运行流程总结:

  • new myPromise -->内部执行(resolve, reject) => {}函数 -->在执行resolve/reject–>then函数

异步问题

上面的的代码如果时同步执行是没有问题的,但是一旦遇到异步,then就会拿不到值

const p = new myPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve('success')
    })
})
p.then((res) => {
    console.log(res)
}, (err) => {
    console.log("捕获",err)
})
//执行后没有打印任何数据

是什么问题导致没有打印我们想要的数据?

  • 根据上面的运行流程我们可以发现 当new myPromise时 ,内部执行(resolve, reject) => {},而外部是一个异步(需要等到下一个循环才能执行),所以就会先执行then 在执行 resolve/ reject方法,所以就是最后两步顺序颠倒导致的错误,所以我们只需要将步骤调换即可。

如何解决异步问题?

  • 我们可以用setTimeout比上一个延时时间长一点就可以拿到(显然是一个不好的方法)
  • 发布订阅模式(即then方法时传入的函数不执行,收集到一个数组中,当 resolve/ reject被调用时再去执行then传入的函数)

发布订阅模式处理异步问题

//解决异步
class myPromise{
    constructor(executor) {
        this.state ='pending'; //初始化状态
        this.returnVal =undefined; //成功返回值
        this.retReson = undefined; //失败返回值
        this.onResolvedCallbacks = [] //成功的回调函数
        this.onRejectedCallbacks = [] //失败的回调函数
        // 成功方法
        let resolve = (value) => {
             //将状态修改为成功,并存储当前传递的参数
            if (this.state == 'pending') {
                this.state = 'fullFilled'
                this.value = value
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        // 失败方法
        let reject = (reason) => {
            //将状态修改为失败,并存储当前传递的参数
            if (this.state == 'pending') {
                this.state = 'rejected'
                this.reason = reason
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        //保证函数执行时不会报错,如果保存直接调用失败方法
        try {
            // 执行函数,外接传递两个函数供其执行
            executor(resolve, reject)
        } catch (err) {
            // 失败则直接执行reject函数
            reject(err)
        }
    }
    /**
    * then 传递两个函数
     * onFullFilled 成功方法
     * onRejected   失败方法
    * */
    then(onFullFilled, onRejected){
        // 同步 状态为fulfuilled,执行onFullFilled,传入成功的值
        if (this.state == 'fullFilled') {
            onFullFilled(this.value)
        }
        // 同步状态为rejected,执行onRejected,传入失败的值
        if (this.state == 'rejected') {
            onRejected(this.reason)
        }
        //异步时状态为pending
        if(this.state == 'pending'){
            /**
             * 收集要执行的函数,这块必须要推入一个函数,
             * 不然就会直接执行后边拿不到数据了
             * */
            //成功时订阅的函数集合
            this.onResolvedCallbacks.push(()=>{
                onFullFilled(this.value)
            })
            //失败时订阅的函数集合
            this.onRejectedCallbacks.push(()=>{
                onRejected(this.reason)
            })
        }
    }
}
const p = new myPromise((resolve, reject) => {
    setTimeout(()=>{
        resolve('success')
    },1500)
})
p.then((res) => {
    console.log(res)
}, (err) => {
    console.log("捕获",err)
})
//-->打印了success

至此关于已经解决了异步问题!

关于then的链式调用问题

我们先来看一下官方的用法

const data= new Promise(((resolve, reject) => {
    resolve(100)
}))
data.then((res)=>{
    console.log("1",res)
    return res*500
}).then((res)=>{
    console.log("2",res)
    return new Promise(((resolve, reject) => {
        resolve(res)
    }))
}).then((res)=>{
    console.log('3',res)
})
最后输出结果为 
1 100
2 50000
3 50000

为什么then能过够链式调用和then里面可能出现的情况?

  • then方法是由在new promise时才会有的,那么我们在返回一个新的promse不就好了?
  • then里面返回的可能是一个值,还有可能时一个新的promse对象

解决then链式调用返回值是一个值得问题

//then的链式调用问题
class myPromise{
    constructor(executor) {
        this.state ='pending'; //初始化状态
        this.returnVal =undefined; //成功返回值
        this.retReson = undefined; //失败返回值
        this.onResolvedCallbacks = [] //成功的回调函数
        this.onRejectedCallbacks = [] //失败的回调函数
        // 成功方法
        let resolve = (value) => {
             //将状态修改为成功,并存储当前传递的参数
            if (this.state == 'pending') {
                this.state = 'fullFilled'
                this.value = value
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        // 失败方法
        let reject = (reason) => {
            //将状态修改为失败,并存储当前传递的参数
            if (this.state == 'pending') {
                this.state = 'rejected'
                this.reason = reason
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        //保证函数执行时不会报错,如果保存直接调用失败方法
        try {
            // 执行函数,外接传递两个函数供其执行
            executor(resolve, reject)
        } catch (err) {
            // 失败则直接执行reject函数
            reject(err)
        }
    }
    /**
    * then 传递两个函数
     * onFullFilled 成功方法
     * onRejected   失败方法
    * */
    then(onFullFilled, onRejected){
        let promise = new myPromise((reslove,reject)=>{
            // 同步 状态为fulfuilled,执行onFullFilled,传入成功的值
            if (this.state == 'fullFilled') {
                reslove(onFullFilled(this.value))

            }
            // 同步状态为rejected,执行onRejected,传入失败的值
            if (this.state == 'rejected') {
                reject(onRejected(this.reason))
            }
            //异步时状态为pending
            if(this.state == 'pending'){
                /**
                 * 收集要执行的函数,这块必须要推入一个函数,
                 * 不然就会直接执行后边拿不到数据了
                 * */
                //成功时订阅的函数集合
                this.onResolvedCallbacks.push(()=>{
                    reslove( onFullFilled(this.value))
                })
                //失败时订阅的函数集合
                this.onRejectedCallbacks.push(()=>{
                   reject(onRejected(this.reason))
                })
            }
        })
        return promise
    }
}

const data= new myPromise(((resolve, reject) => {
    resolve(100)
}))
data.then((res)=>{
    console.log("1",res)
    return res*500
}).then((res)=>{
    console.log("2",res)
    return new myPromise(((resolve, reject) => {
        resolve(res)
    }))
}).then((res)=>{
    console.log('3',res)
})
输出  
1 100
2 50000
3 myPromise {
  state: 'fullFilled',
  returnVal: undefined,
  retReson: undefined,
  onResolvedCallbacks: [],
  onRejectedCallbacks: [],
  value: 50000
}

传递是普通值已经可以正常打印了,但是最后一个返回的是一个promise 对象,所以我们这时候需要处理一下,把promise里面的值拿出来,也就是执行promse即可

解决返回值是一个promse对象问题

	//执行promise 把值拿出来
const resolvePromise = (promise2, temp, resolve, reject) => {
    // x和promise2不能是同一个人,如果是同一个人就报错
    if (promise2 === temp) {
        return reject(
            new TypeError('Chaining cycle detected for promise #<promise>')
        )
    }
    // 判断如果x是否是一个对象,判断函数是否是对象的方法有:typeof instanceof constructor toString
    if (typeof temp === 'object' && temp != null || typeof temp === 'function') {
        try {
            let then = temp.then // 取then可以报错,报错就走reject()
            if (typeof then === 'function') {
                // 用then.call()为了避免在使用一次temp.then报错
                then.call(temp, y => {
                    // console.log('y', y)
                    resolve(y)// 采用promise的成功结果,并且向下传递
                }, r => {
                    reject(r)// 采用promise的失败结果,并且向下传递
                })
            } else {
                resolve(temp)// temp不是一个函数,是一个对象
            }
        } catch (err) {
            reject(err)
        }
    } else {
        // temp是一个普通值
        resolve(temp)
    }
}
class myPromise{
    constructor(executor) {
        this.state ='pending'; //初始化状态
        this.returnVal =undefined; //成功返回值
        this.retReson = undefined; //失败返回值
        this.onResolvedCallbacks = [] //成功的回调函数
        this.onRejectedCallbacks = [] //失败的回调函数
        // 成功方法
        let resolve = (value) => {
             //将状态修改为成功,并存储当前传递的参数
            if (this.state == 'pending') {
                this.state = 'fullFilled'
                this.value = value
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        // 失败方法
        let reject = (reason) => {
            //将状态修改为失败,并存储当前传递的参数
            if (this.state == 'pending') {
                this.state = 'rejected'
                this.reason = reason
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        //保证函数执行时不会报错,如果保存直接调用失败方法
        try {
            // 执行函数,外接传递两个函数供其执行
            executor(resolve, reject)
        } catch (err) {
            // 失败则直接执行reject函数
            reject(err)
        }
    }
    /**
    * then 传递两个函数
     * onFullFilled 成功方法
     * onRejected   失败方法
    * */
    then(onFullFilled, onRejected){
        let promise = new myPromise((reslove,reject)=>{
            // 同步 状态为fulfuilled,执行onFullFilled,传入成功的值
            let temp ;
            if (this.state == 'fullFilled') {
            		//promise 需要等到下一个循环才能拿到所以用了setTimeout,
            		//也可以用process.next
                setTimeout(()=>{
                        try {
                            temp = onFullFilled(this.value)
                            resolvePromise(promise,temp, reslove, reject)
                        }
                        catch (err){
                            reject(err)
                        }
                },0)
            }
            // 同步状态为rejected,执行onRejected,传入失败的值
            if (this.state == 'rejected') {
                setTimeout(()=>{
                    try {
                        temp =onRejected(this.reason)
                        resolvePromise(promise,temp, reslove, reject)
                    }
                    catch (err){
                        reject(err)
                    }
                },0)
            }
            //异步时状态为pending
            if(this.state == 'pending'){
                /**
                 * 收集要执行的函数,这块必须要推入一个函数,
                 * 不然就会直接执行后边拿不到数据了
                 * */
                //成功时订阅的函数集合
                this.onResolvedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try {
                            temp = onFullFilled(this.value)
                            resolvePromise(promise,temp, reslove, reject)
                        }catch (err){
                            reject(err)
                        }
                    },0)
                })
                //失败时订阅的函数集合
                this.onRejectedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try {
                            temp = onRejected(this.reason)
                            resolvePromise(promise,temp, reslove, reject)
                        }catch (err){
                            reject(err)
                        }
                    },0)
                })
            }
        })
        return promise
    }
}

const data= new myPromise(((resolve, reject) => {
    resolve(100)
}))
data.then((res)=>{
    console.log("1",res)
    return res*500
}).then((res)=>{
    console.log("2",res)
    return new myPromise(((resolve, reject) => {
        resolve(res)
    }))
},(err)=>{
     console.log(err)
}).then((res)=>{
    console.log('3',res)
})
输出
1 100
2 50000
3 50000

至此核心功能已经实现了!

细节总结:

then方法中为什么要用setTimeout?因为在当前函数内部是无法拿到外部promise这个变量的,需要等到下一次循环事件之后才可以拿到。

catch用法

new Promise((resolve, reject)=>{
    reject('errs')
}).then((res)=>{
},(err)=>{
    console.log('捕获err',err)
}).catch((errs)=>{
    console.log("catch",errs)
})
如果then中,有第二个错误函数的话,catch不会打印输入,
如果没有的话catch 就会执行

catch核心代码

myPromise.prototype.catch = function(onReJected) {
    return this.then(undefined, onReJected)
}
只需要让它调用then方法中的错误时要执行的方法即可

由于then方法需要传递两个函数所以需要改成可选参数

 //如果 传递一个参数,调用then就会因为少一个函数而报错,所以需要认为的赋值一个函数供他执行
 onRejected = isFunction(onRejected) ? onRejected : err => {
            throw err
        }

修改过后的全部代码

/**
 * 整理过后的完整代码
 */
const isFunction = (value) => typeof value === 'function'
const PENDING = 'pending';
const FULLFILLED ='fullFilled';
const REJECTED ='rejected';
const resolvePromise = (promise2, temp, resolve, reject) => {
    // x和promise2不能是同一个人,如果是同一个人就报错
    if (promise2 === temp) {
        return reject(
            new TypeError('Chaining cycle detected for promise #<promise>')
        )
    }
    // 判断如果x是否是一个对象,判断函数是否是对象的方法有:typeof instanceof constructor toString
    if (typeof temp === 'object' && temp != null || typeof temp === 'function') {
        try {
            let then = temp.then // 取then可以报错,报错就走reject()
            if (typeof then === 'function') {
                // 用then.call()为了避免在使用一次temp.then报错
                then.call(temp, y => {
                    //返回的时一个promise对象,所以需要再次调用
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {
                    reject(r)// 采用promise的失败结果,并且向下传递
                })
            } else {
                resolve(temp)// x不是一个函数,是一个对象
            }
        } catch (err) {
            reject(err)
        }
    } else {
        // temp是一个普通值
        resolve(temp)
    }
}
class myPromise{
    constructor(executor) {
        this.state =PENDING; //初始化状态
        this.returnVal =undefined; //成功返回值
        this.retReson = undefined; //失败返回值
        this.onResolvedCallbacks = [] //成功的回调函数
        this.onRejectedCallbacks = [] //失败的回调函数
        // 成功方法
        let resolve = (value) => {
            //将状态修改为成功,并存储当前传递的参数
            if (this.state === PENDING) {
                this.state = FULLFILLED
                this.value = value
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }
        // 失败方法
        let reject = (reason) => {
            //将状态修改为失败,并存储当前传递的参数
            if (this.state === PENDING) {
                this.state = REJECTED
                this.reason = reason
                this.onRejectedCallbacks.forEach(fn => fn())
            }
        }
        //保证函数执行时不会报错,如果保存直接调用失败方法
        try {
            // 执行函数,外接传递两个函数供其执行
            executor(resolve, reject)
        } catch (err) {
            // 失败则直接执行reject函数
            reject(err)
        }
    }
    /**
     * then 传递两个函数
     * onFullFilled 成功方法
     * onRejected   失败方法
     * */
    then(onFullFilled, onRejected){
        onFullFilled = isFunction(onFullFilled) ? onFullFilled : data => data
        onRejected = isFunction(onRejected) ? onRejected : err => {
            throw err
        }
        let promise = new myPromise((reslove,reject)=>{
            // 同步 状态为fulfuilled,执行onFullFilled,传入成功的值
            let temp ;
            if (this.state === FULLFILLED) {
                setTimeout(()=>{
                    try {
                        temp = onFullFilled(this.value)
                        resolvePromise(promise,temp, reslove, reject)
                    }
                    catch (err){
                        reject(err)
                    }
                },0)
            }
            // 同步状态为rejected,执行onRejected,传入失败的值
            if (this.state === REJECTED) {
                setTimeout(()=>{
                    try {
                        temp =onRejected(this.reason)
                        // console.log('temp',temp)
                        resolvePromise(promise,temp, reslove, reject)
                    }
                    catch (err){
                        console.log('err',err)
                        reject(err)
                    }
                },0)
            }
            //异步时状态为pending
            if(this.state === PENDING){
                /**
                 * 收集要执行的函数,这块必须要推入一个函数,
                 * 不然就会直接执行后边拿不到数据了
                 * */
                //成功时订阅的函数集合
                this.onResolvedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try {
                            temp = onFullFilled(this.value)
                            resolvePromise(promise,temp, reslove, reject)
                        }catch (err){
                            reject(err)
                        }
                    },0)

                })
                //失败时订阅的函数集合
                this.onRejectedCallbacks.push(()=>{
                    setTimeout(()=>{
                        try {
                            temp = onRejected(this.reason)
                            resolvePromise(promise,temp, reslove, reject)
                        }catch (err){
                            reject(err)
                        }
                    },0)
                })
            }
        })
        return promise
    }
}
myPromise.prototype.catch = function(onReJected) {
    // 返回一个没有第一个参数的then方法
    return this.then(undefined, onReJected)
}
new myPromise((resolve, reject)=>{
    reject('errs')
}).then(undefined,(erss)=>{
    console.log('AAA',erss)
}).catch((errs)=>{
    console.log("catch",errs)
})
直接catch捕获即可拿到reject返回得数据

promise.all 方法

官方用例

//同步
const  a = new Promise((reslove,reject)=>{
    reslove('a')
})
const  b = new Promise((reslove,reject)=>{
    reject('b')
})
Promise.all([a,b]).then((res)=>{
     console.log(res)
}).catch((err)=>{
    console.log('err',err)
})

//异步
const  a = new Promise((reslove,reject)=>{
    setTimeout(()=>{
        reslove('a')
    },1000)
})
const  b = new Promise((reslove,reject)=>{
    setTimeout(()=>{
        reject('b')
    },1500)
})
Promise.all([a,b]).then((res)=>{
    console.log(res)
}).catch((err)=>{
    console.log('err',err)
})

当a和b同时reslove时,all方法才会成功,否则就会被cathc 捕获,如果传入某一个值不是promise对象那就会这个值不会执行会连同promise得结果返回来,遇到错误,返回错误结束。

//判断是否时promise 对象
const isPromise = (value) => {
     //value得值不为null 且 typeof 是个obj, 或者是一个函数,在判断value 是否有then方法
    if ((value != null && typeof value === 'object') || typeof value === 'function') {
        if (typeof value.then == 'function') {
            return true
        }else  {
            return  false
        }
    } else {
        return false
    }
}
 static all =(lists)=>{
        return new myPromise((resolve, reject) => {
            let resDataArr = [];
            let index = 0;
            let listlen =lists.length;
            function processData(i,data){
                resDataArr[i] = data
                index++;
                //如果index 等于lists长度代表最后一个执行完毕可以抛出正确数据
                if (index === listlen){
                       resolve(resDataArr)
                }
            }
            for (let i = 0; i <listlen ; i++) {
                if(isPromise(lists[i])){
                    lists[i].then((data) => {
                        //此处不能直接resolve,直接执行只能拿到第一个promise得结果,需要临时存储
                        processData(i, data)
                    }, (err) => {
                        //遇到错误直接停止
                        reject(err)
                        return
                    })
                }else  {
                    processData(i, lists[i])
                }
            }
        })
    }

all方法,能够处理异步和传入的值不是一个promise得处理。

race 实现

原理:循环执行传入的promise ,不是promise 直接输出,如果是promise的话谁先执行先输出谁即可。

 static race =(list)=>{
        return new myPromise((reslove,reject)=>{
            let len = list.length;
            for (let i = 0; i <len ; i++) {
                console.log("!!!@@@")
                if(isPromise(list[i])) {
                    list[i].then((data) => {
                        reslove(data)
                        return
                    }, (err) => {
                        reject(err)
                        return
                    })

                }else  {
                    //非promise直接执行reslove出去
                    reslove(list[i])
                }
            }
        })
    }

目前这个方法有些缺陷,就是如果第一个参数时promise且是异步,第二个是一个函数/其他值时,会打印出第二个值,因为第一个是异步,所以第二个先执行完,所以会打印第二个值,官方中则不会。有兴趣的效果伙伴可以实现一下或者提供一下解决方案!

resolve方法

判断试下传入的值是否是一个promise对象如果是直接返回,如果不是返回一个新的promise对象

 static resolve(val){
        if(isPromise(val)){
            return  val
        } else {
            return new myPromise((reslove,reject)=>{
                reslove(val)
            })
        }
    }

resolve方法

与resolve 方法原理相同

static reject(val){
        if(isPromise(val)){
            return  val
        }else {
            return new myPromise((reslove,reject)=>{
                reject(val)
            })
        }
    }

##finally 方法
在ES6中提到(https://es6.ruanyifeng.com/#docs/promise#Promise-prototype-finally)
finally本质上是then方法的特例

myPromise.prototype.finally =function (callback){
    let P = this.constructor;
    //无论是成功还是失败都会走传入得callback函数
    return this.then(
        value  => P.resolve(callback()).then(() => value),
        reason => P.resolve(callback()).then(() => { throw reason })
    );
}

本文参考某位大神博客,下面是地址https://www.cnblogs.com/xinggood/p/11836096.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值