其他分享
首页 > 其他分享> > JS Promise的用法, 以及自己模拟一个Promise

JS Promise的用法, 以及自己模拟一个Promise

作者:互联网

注: 本文中写的类只是为了了解Promise类的内部原理而模拟出来一个, 并不一定符合类似的规范或者效率多么高, 但是基本的功能还是实现了的.

用法

如下, 这是一个传统的使用回调函数的异步代码

function getAnInt(callback) {
    setTimeout(() => {
        callback(81)
    }, 500)
}

function sqrt(n, resolve, reject) {
    setTimeout(() => {
        let res = Math.sqrt(n)
        if (parseInt(res) === res) {
            resolve(Math.sqrt(n))
        } else {
            reject("cannot get an int")
        }
    }, 500)
}

let errHandler = err => console.log("Error " + err)

getAnInt(v1 => {
    console.log(v1)
    sqrt(v1, v2 => {
        console.log(v2)
        sqrt(v2, v3 => {
            console.log(v3)
            sqrt(v3, v4 => {
                console.log(v4)
            }, errHandler)
        }, errHandler)
    }, errHandler)
})

执行结果:

81
9
3
Error cannot get an int

有没有感觉眼花缭乱? 这金字塔状的代码被亲切地称为回调地狱, 下面就是我们的主角Promise上场的时候了, 酱酱酱酱

function getAnInt() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(81)
        }, 500)
    })
}

function sqrt(n) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            let res = Math.sqrt(n)
            if (parseInt(res) === res) {
                resolve(Math.sqrt(n))
            } else {
                reject("cannot get an int")
            }
        }, 500)
    })
}


getAnInt().then(v1 => {
    console.log(v1)
    return sqrt(v1)
}).then(v2 => {
    console.log(v2)
    return sqrt(v2)
}).then(v3 => {
    console.log(v3)
    return sqrt(v3)
}).then(v4 => {
    console.log(v4)
}).catch(err => {
    console.log("Error " + err)
})

执行结果:

81
9
3
Error cannot get an int

结果一模一样, 但是这个代码写出来的感觉, 就是要清晰了好多好多好多好多好多好多

介绍

Promise/A+标准中定义了Promise到底是个什么东西, 这里挑出重点部分, 其余的规范如果想看的话点这里去往官网https://promisesaplus.com/

  • promise 含有then方法, 没有规定其它的方法.
  • then方法会返回一个新的promise
  • promise有三个状态, pending(代办), fulfilled(完成)rejected(被拒绝), 状态只能从pending转成另外两个, 然后就不能再转了.
  • 如果onRejected或者onFulfilled返回了一个Promise对象, 需要得出它的结果再传给下一个then方法里对应的地方

因为本文代码中有很多的 resolve, 所以这里的代码使用resolved(被解决)代替fulfilled

为什么没有列出来更多的内容呢, 因为其它的内容大多和兼容性有关, 与这个实现原理关系不是太大, 还有的是到具体实现函数的时候才会用到的规范, 所以我没有列出来

注: catch方法是ES6标准里的, 它的原理是then(null, onRejected)

实现

住: 本文代码不考虑throw, 为了只体现原理, 让代码尽可能更简单.

Promise的构造函数通常传入一个执行者函数, 这个函数里面是异步逻辑, 接受两个参数: resolvereject.

好, 下面开始做点准备工作

const Pending  = 'pending'
const Resolved = 'resolved'
const Rejected = 'rejected'

class MyPromise {}

诶, 这段代码我感觉不用解释了吧? 下面的我会在注释或者是代码块下方说明


class MyPromise {
    constructor(executor) {
        // 状态
        this.status = Pending
        // 正常运行返回的结果
        this.value = null
        // 发生错误的原因
        this.reason = null
        // 详见这段代码块下面写的 注1
        this.onRejected = () => {}
        this.onResolved = () => {}
        
        let resolve = value => {
            // 如果不是Pending就忽略
            if (this.status !== Pending) {
                return
            }
            this.status = Resolved
            this.value = value
            this.onResolved(value)
        }

        let reject = reason => {
            // 如果不是Pending就忽略
            if (this.status !== Pending) {
                return
            }
            this.status = Rejected
            this.reason = reason
            this.onRejected(reason)
        }
        // 见 注2
        executor(resolve, reject)
    }
}

然后就是then方法啦~

注意: then方法要求每次返回新的Promise对象.

先写个框架

then(onResolved, onRejected) {
  let funcOrNull = f => typeof f === "function" ? f : null
  onResolved = funcOrNull(onResolved)
  onRejected = funcOrNull(onRejected)

  if (this.status === Rejected) {
    return new MyPromise((resolve, reject) => {

    })
  } else if (this.status === Resolved) {
    return new MyPromise((resolve, reject) => {

    })
  } else {
    return new MyPromise((resolve, reject) => {

    })
  }
}

这一段应该没什么不好理解的地方, 然后先实现第一个if块里的代码

if (this.status === Rejected) {
    return new MyPromise((resolve, reject) => {
        let value = (onRejected === null ? reject : onRejected)(this.reason)
        if (value instanceof MyPromise) {
            value.then(resolve, reject)
        } else {
            resolve(value)
        }
    })
}

这些实现的代码包括下面的elseif和else块就是最难理解的了, 我当时是好久好久也没有理解, 接下来我会就像数学里面一样分类讨论:




如果上面的都能理解了, 那么下面这个elseif块就特别好理解了

else if (this.status === Resolved) {
    return new MyPromise((resolve, reject) => {
        let value = (onResolved === null ? resolve : onResolved)(this.value)
        if (value instanceof MyPromise) {
            value.then(resolve, reject)
        } else {
            resolve(value)
        }
    }
} 

else块里, 也就是状态是Pending的时候, 需要做的事情几乎和上面的ifelseif块一样

Promise对象状态是Pending的时候, 不能通过this.valuethis.reason获取值, 但是, 我们可以通过设置this.onRejectedthis.onResolved这两个函数, 因为当Promiseexecutor执行完的时候一定会调用这两个函数中的一个, 并且调用它们的时候都会带上valuereason, 所以这里的代码需要这么写

else {
    return new MyPromise((resolve, reject) => {
        this.onResolved = value => {
            let v = (onResolved === null ? resolve : onResolved)(value)
            if (v instanceof MyPromise) {
                v.then(resolve, reject)
            } else {
                resolve(v)
            }
        }

        this.onRejected = reason => {
            let v = (onRejected === null ? reject : onRejected)(reason)
            if (v instanceof MyPromise) {
                v.then(resolve, reject)
            } else {
                resolve(v)
            }
        }
    })
}

最后加上一个catch方法, 其实就是一个语法糖, 既然ES6都加上了, 那我也加上吧

catch(onRejected) {
    return this.then(null, onRejected)
}

嘿咻, 终于弄完了, 接下来就是实验新对象的时候啦!(这么说好像有点怪怪的呢)

还是文章开头那熟悉的味道

function getAnInt() {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve(81)
        }, 500)
    })
}

function sqrt(n) {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            let res = Math.sqrt(n)
            if (parseInt(res) === res) {
                resolve(Math.sqrt(n))
            } else {
                reject("cannot get an int")
            }
        }, 500)
    })
}


getAnInt().then(v1 => {
    console.log(v1)
    return sqrt(v1)
}).then(v2 => {
    console.log(v2)
    return sqrt(v2)
}).then(v3 => {
    console.log(v3)
    return sqrt(v3)
}).then(v4 => {
    console.log(v4)
}).catch(err => {
    console.log("Error " + err)
})

结果

81
9
3
Error cannot get an int

附: 全代码

const Pending  = 'pending'
const Resolved = 'resolved'
const Rejected = 'rejected'

class MyPromise {
    constructor(executor) {
        // 状态
        this.status = Pending
        // 正常运行返回的结果
        this.value = null
        // 发生错误的原因
        this.reason = null
        // 见 注1
        this.onRejected = () => {}
        this.onResolved = () => {}
        
        let resolve = value => {
            // 如果不是Pending就忽略
            if (this.status !== Pending) {
                return
            }
            this.status = Resolved
            this.value = value
            this.onResolved(value)
        }

        let reject = reason => {
            // 如果不是Pending就忽略
            if (this.status !== Pending) {
                return
            }
            this.status = Rejected
            this.reason = reason
            this.onRejected(reason)
        }
        // 见 注2
        executor(resolve, reject)
    }

    then(onResolved, onRejected) {
        let funcOrNull = f => typeof f === "function" ? f : null
        onResolved = funcOrNull(onResolved)
        onRejected = funcOrNull(onRejected)

        if (this.status === Rejected) {
            return new MyPromise((resolve, reject) => {
                let value = (onRejected === null ? reject : onRejected)(this.reason)
                if (value instanceof MyPromise) {
                    value.then(resolve, reject)
                } else {
                    resolve(value)
                }
            })
        } else if (this.status === Resolved) {
            return new MyPromise((resolve, reject) => {
                let value = (onResolved === null ? resolve : onResolved)(this.value)
                if (value instanceof MyPromise) {
                    value.then(resolve, reject)
                } else {
                    resolve(value)
                }
            })
        } else {
            return new MyPromise((resolve, reject) => {
                this.onResolved = value => {
                    let v = (onResolved === null ? resolve : onResolved)(value)
                    if (v instanceof MyPromise) {
                        v.then(resolve, reject)
                    } else {
                        resolve(v)
                    }
                }

                this.onRejected = reason => {
                    let v = (onRejected === null ? reject : onRejected)(reason)
                    if (v instanceof MyPromise) {
                        v.then(resolve, reject)
                    } else {
                        resolve(v)
                    }
                }
            })
        }
    }

    catch(onRejected) {
        return this.then(null, onRejected)
    }
}

// 测试模块!
function getAnInt() {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve(81)
        }, 500)
    })
}

function sqrt(n) {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            let res = Math.sqrt(n)
            if (parseInt(res) === res) {
                resolve(Math.sqrt(n))
            } else {
                reject("cannot get an int")
            }
        }, 500)
    })
}


getAnInt().then(v1 => {
    console.log(v1)
    return sqrt(v1)
}).then(v2 => {
    console.log(v2)
    return sqrt(v2)
}).then(v3 => {
    console.log(v3)
    return sqrt(v3)
}).then(v4 => {
    console.log(v4)
}).catch(err => {
    console.log("Error " + err)
})

参考: https://zhuanlan.zhihu.com/p/21834559
https://zhuanlan.zhihu.com/p/183801144

标签:resolve,return,reason,value,用法,onRejected,Promise,reject,JS
来源: https://www.cnblogs.com/nekodaisuki/p/14401427.html