注: 本文中写的类只是为了了解Promise
类的内部原理而模拟出来一个, 并不必定符合相似的规范或者效率多么高, 可是基本的功能仍是实现了的.
注: 本文代码运行环境: NodeJS v14.9.0数组
以下, 这是一个传统的使用回调函数的异步代码promise
function getAnInt(callback) { setTimeout(() => { callback(81) }, 500) } function sqrt(n, resolve, reject) { setTimeout(() => { let res = Math.sqrt(n) if (parseInt(res) === res) { resolve(res) } 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(res) } 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
究竟是个什么东西, 这里挑出重点部分, 其他的规范若是想看的话点这里去官网this
promise
含有then
方法, 没有规定其它的方法.then
方法会返回一个新的promise
then
方法的参数是onFulfilled, onRejected
, 它们都是可选的(固然都是函数类型)promise
有三个状态,pending(代办)
,fulfilled(完成)
和rejected(被拒绝)
, 状态只能从pending
转成另外两个, 而后就不能再转了.- 若是
onRejected
或者onFulfilled
返回了一个Promise
对象, 须要得出它的结果再传给下一个then
方法里对应的地方
由于本文代码中有不少的 resolve
, 因此这里的代码使用resolved(被解决)
代替fulfilled
code
为何没有列出来更多的内容呢, 由于其它的内容大多和兼容性有关, 与这个实现原理关系不是太大, 还有的是到具体实现函数的时候才会用到的规范, 因此我没有列出来对象
注: catch
方法是ES6标准里的, 它的原理是then(null, onRejected)
get
注: 本文代码不考虑throw
, 为了只体现原理, 让代码尽量更简单.
Promise
的构造函数一般传入一个执行者函数, 这个函数里面多是异步逻辑(这么说的意思就是也可能不是), 接受两个参数: resolve
和reject
.
调用resolve(value)
就表明方法成功执行了, Promise
会把resolve
中传入的value
传给then
方法里的参数
调用reject(reason)
就是执行出错了, Promise
会把reject
中传入的reason
传给then
方法里的参数
好, 下面开始作点准备工做
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) } }
注1: 这是两个被reject
或者resolve
后调用的回调函数, 我看的别人实现的版本大可能是一个数组, 而后调用的时候一个接一个调用里面的函数.
我认为对同一个promise调用屡次then
方法的时候不多, 并且本文只是一个思路展现, 并不严格遵照A+规范, 因此这里就直接写了个什么也没干的函数
在这里也分析一下, 在then
方法调用的时候, 若是调用then
时的状态是Pending
, 那么就设置一下当前对象里的onRejected
和onResolved
, 具体设置什么在后面的代码里会提到; 若是状态不是Pending
, 就表明这两个函数早就执行完了, 就须要根据this.value
和this.reason
具体的调用then
函数中传进来的onRejected
和onResolved
.
注2: 这里直接同步调用了, 没有异步调用. 由于若是这个操做真的须要异步的话, 在executor
函数里面就会有异步方法了(如setTimeout
), 不须要Promise
类给它办.
而后就是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) => { }) } }
若是是状态是rejected, 那么
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块就是最难理解的了, 我当时是很久很久也没有理解, 接下来我会就像数学里面同样分类讨论:
理解了Rejected块, 那么Resolved块和他几乎如出一辙, 只是函数名字不同而已, 因此我这里会分析的尽量详细
若是调用的时候是这样的:
new MyPromise((resolve, reject) => { reject("I rejected the promise") }).then(null, console.log)
先分析构造方法, 建立Promise
对象的时候, 这里它的状态就变成Rejected
, 可是其余的什么事都没干, 让咱们来看前面的代码
this.onRejected = () => {} this.onResolved = () => {} let reject = reason => { if (this.status !== Pending) { return } this.status = Rejected this.reason = reason this.onRejected(reason) } executor(resolve, reject)
这个时候this.onRejected
仍是个空函数, 因此调用它也没什么用
接下来到then
方法了, 让咱们来看上面if块里的代码
return new MyPromise((resolve, reject) => { let value = (onRejected === null ? reject : onRejected)(this.reason) if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) } })
能够看出它执行了let value = onRejected(reason)
, 而后调用resolve(value)
, 以后这个新的Promise
状态就是Resolved
了.
至于为何这里要用resolve
, 我是经过NodeJS作了个实验看看NodeJS对这件事是怎么干的, 代码以下
let p1 = new Promise((resolve, reject) => { reject("I rejected the promise") }) let p2 = p1.then(null, reason => { return 'I am from onRejected function' }) // 这里是为了避免管究竟是什么状态都能把p1和p2输出出来 p2.then(() => console.log(p1, p2), () => console.log(p1, p2))
输出(本来的执行结果没有换行, 我为了方便看本身加上的)
Promise { <rejected> 'I rejected the promise' } Promise { 'I am from onRejected function' }
这就看出来NodeJS是在处理完错误以后把onRejected
的返回值用resolve
函数处理了
若是调用的时候是这样的
new MyPromise((resolve, reject) => { reject("I just rejected the promise") }).then(null, null).then(null, console.log)
这个时候就要考虑不能把错误信息丢掉了, 为了实现这个"穿透"功能, 咱们能够研究一下NodeJS是怎么干的
let p1 = new Promise((resolve, reject) => { reject("I rejected the promise") }).then(null, null) p1.then(() => console.log(p1), () => console.log(p1))
输出
Promise { <rejected> 'I rejected the promise' }
这就很简单了, NodeJS是把新的Promise
对象继续调用reject
而且传递错误信息. 因此再看上面if块里的代码
return new MyPromise((resolve, reject) => { let value = (onRejected === null ? reject : onRejected)(this.reason) if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) } })
能够看出这里也是在onRejected
空的时候直接用reject
方法把新的Promise
对象的状态设置成了Rejected
而且也把this.reason
错误信息传了过去.
展示成代码的话, 就是执行了reject(this.reason)
你可能会疑惑, 那么reject(this.reason)
返回值应该是undefined
, 而后又调用了resolve(value)
是怎么回事呢?
这里咱们要看前面的代码
let resolve = value => { // 若是不是Pending就忽略 if (this.status !== Pending) { return } this.status = Resolved this.value = value this.onResolved(value) }
在调用完reject
以后, 这里的status
就变成了Rejected
, 这个方法就不会调用了呀
你可能还会疑惑, 这里的代码
if (value instanceof MyPromise) { value.then(resolve, reject) } else { resolve(value) }
虽说你知道返回值是Promise
要得出结果, 但这是onRejected
返回的值, 为何第二行要这么写?
仍是老方法, 咱们看看NodeJS这个地方怎么实现的
let p = new Promise((resolve, reject) => { reject("I rejected the promise") }).then(null, reason => { return new Promise((resolve, reject) => { resolve("Hello~") }) }).then(value => { console.log("Value " + value) }, reason => { console.log("Reason " + reason) })
运行结果
Value Hello~
因此说, 这里须要这么写, 让这个then
里返回的Promise
对象then
方法的onResolved
方法直接调用新对象的resolve
和reject
方法来操做这个新对象
若是上面的都能理解了, 那么下面这个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
的时候, 须要作的事情几乎和上面的if
和elseif
块同样
在Promise
对象状态是Pending
的时候, 不能经过this.value
和this.reason
获取值, 可是, 咱们能够经过设置this.onRejected
和this.onResolved
这两个函数, 由于当Promise
的executor
执行完的时候必定会调用这两个函数中的一个, 而且调用它们的时候都会带上value
和reason
, 因此这里的代码须要这么写
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(res) } 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(res) } 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