注: 本文中写的类只是为了了解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
的构造函数通常传入一个执行者函数, 这个函数里面是异步逻辑, 接受两个参数: 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) => {
})
}
}
这一段应该没什么不好理解的地方, 然后先实现第一个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块就是最难理解的了, 我当时是好久好久也没有理解, 接下来我会就像数学里面一样分类讨论:
-
如果调用的时候是这样的:
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)
返回值应该是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
要得出结果, 但是为什么第二行要这么写?还是老方法, 我们看看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(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