Promise应该长什么样
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 1000)
}
)
console.log(p)
在控制台上可以看到
PromiseState就是Promise对象的状态
PromiseResult是state为fulfilled时resolve的value 或 为rejected时reject的reason
看一下Promise对象原型
可以看到原型上有这么三个方法
then
catch
finally
再看一下Promise构造函数
可以看到构造函数身上有这么几个方法
all
allSettled
any
race
reject
resolve
所以,Promise是什么,长什么样,已经很清晰了。
Promise是一个class,原型上有then
,catch
,finnaly
方法,类上有这么六个静态方法all
,allSettled
,any
,race
,reject
,resolve
Promise初步形式实现
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1)
resolve(100)
}, 1000)
}
)
通过这段真正Promise构造函数的调用,我们知道了些什么
- Promise的构造函数需要传入一个参数,参数类型为function,称之为executor
- 该函数接收resolve,reject两个参数(类型都为function),client要在函数body中使用这两个函数实现状态的凝固,故这两个函数是Promise自己来实现的
- 打开控制台的话,会发现打印了一个1,知
new Promise
的同时,会执行executor函数体中的内容
class MyPromise {
constructor(executor) {
const resolve = (value) => {
// do something
}
const reject = (reason) => {
// do something
}
// 执行executor函数
executor(resolve, reject)
}
}
这样就形式上实现了new Promise
的操作
Promise中then方法的实现
let p = new Promise((resolve, reject) => {
resolve(100)
}
)
p.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})
阅读这段代码,结合我们知道的Promise知识,得到以下要点
- Promise对象有三个状态,
pending
,fulfilled
,rejected
- 调用resolve方法后,状态从
pending
变为fulfilled
并凝固,保存传入的value - 调用reject方法后,状态从
pending
变为rejected
并凝固,保存传入的reason - then方法有两个参数,
onfulfilled
和onrejected
,都是function,分别作为Promise状态为fulfilled
和rejected
的回调。 onfulfiiled
接收一个参数value,onrejected
接收一个参数reason
class MyPromise {
constructor(executor) {
// 为Promise对象添加state属性,初始为'pending'
this.state = 'pending'
// 为Promise对象添加value属性,作为成功时的结果
this.value = undefined
// 为Promise对象添加reason属性,作为失败时的结果
this.reason = undefined
const resolve = (value) => {
// 状态凝固
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
}
}
const reject = (reason) => {
//状态凝固
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
}
}
executor(resolve, reject)
}
then(onfulfilled, onrejected) {
if (this.state === 'fulfilled') {
onfulfilled(this.value)
}
if (this.state === 'rejected') {
onrejected(this.reason)
}
}
}
好了,至此为此,是一个粗略的实现,可以满足同步执行
now have a try!
let p = new MyPromise((resolve, reject) => {
resolve(100)
}
)
p.then(value => console.log(value))
控制台结果是100
但不支持异步,举个例子
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 1000)
}
)
p.then(value => console.log(value))
控制台什么结果也没有
为什么?非常简单,new Promise
时启动了setTimeout,然后执行p.then
方法,这时候p中还没有调用过resolve方法,即p.state仍为pending,在then方法的逻辑中,没有可供执行的代码,传入的onfulfilled函数并不会被调用
问题来了,怎么让它支持异步?
then中传入的回调函数,需要在状态改变时执行
对应地,就是Promise中定义的resolve和reject方法
但为了支持多次.then
方法的调用,应当将回调函数放入一个数组中,而不是覆盖
class MyPromise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
// 保存成功的回调函数
this.onResolvedCallbacks = []
// 保存失败的回调函数
this.onRejectedCallbacks = []
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
// 按顺序执行所有的成功的回调函数
this.onResolvedCallbacks.forEach(fn => fn())
}
}
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
// 按顺序执行所有的失败的回调函数
this.onRejectedCallbacks.forEach(fn => fn())
}
}
executor(resolve, reject)
}
then(onfulfilled, onrejected) {
if (this.state === 'fulfilled') {
onfulfilled(this.value)
}
if (this.state === 'rejected') {
onrejected(this.reason)
}
if (this.state === 'pending') {
// 存储回调函数
// 在执行时,自动传入执行时刻的this.value
this.onResolvedCallbacks.push(() => onfulfilled(this.value))
this.onRejectedCallbacks.push(() => onrejected(this.reason))
}
}
}
have a try
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 1000)
}
)
p.then(value => console.log(value, 1))
p.then(value => console.log(value, 2))
结果为100 1 ; 100 2
支持链式调用
想要支持链式调用,显而易见地,then方法需要返回一个Promise对象,初步编码如下
class MyPromise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.onResolvedCallbacks.forEach(fn => fn())
}
}
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn())
}
}
executor(resolve, reject)
}
then(onfulfilled, onrejected) {
let p2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 得到成功回调的返回值,并传给p2,使p2 fulfilled
let x = onfulfilled(this.value)
resolve(x)
}
if (this.state === 'rejected') {
let x = onrejected(this.reason)
resolve(x)
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
// 处理异步时同理
let x = onfulfilled(this.value)
resolve(x)
})
this.onRejectedCallbacks.push(() => {
let x = onrejected(this.reason)
resolve(x)
})
}
})
return p2
}
}
have a try
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 1000)
}
)
p.then(value => {
console.log(value)
return 200
}).then(value => console.log(value))
结果为100 200
但我们知道,onfulfilled方法的返回值会被包装上Promise返回出then,但如果onfulfilled中返回的是Promise对象,就不会再封装,对多层嵌套亦然。
在原本的Promise中,应该有
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 1000)
}
)
p.then(value => {
console.log(value)
return new Promise((resolve, reject) => {
resolve(200)
})
}).then(value => console.log(value))
结果是100 200,但使用刚才封装的MyPromise显然不是,p.then的返回值是一个嵌套两层的Promise
其实在原本Promise对象中,如果发生Promise嵌套,都会被解析只剩下一层
譬如
Promise.resolve(Promise.resolve(Promise.resolve(100))).then(console.log)
控制台会直接输出100
故不应该对返回值x作判断,不妨在resolve和reject方法上下文章。修改如下
class MyPromise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
// 定义箭头函数
const handleResolve = x => {
if (x instanceof MyPromise) {
// 将外层resolve的值传入handleResolve递归
x.then(handleResolve)
} else {
this.state = 'fulfilled'
this.value = x
this.onResolvedCallbacks.forEach(fn => fn())
}
}
if (this.state === 'pending') {
handleResolve(value)
}
}
const reject = (reason) => {
const handleReject = x => {
if (x instanceof MyPromise) {
x.then(handleReject)
} else {
this.state = 'rejected'
this.reason = x
this.onRejectedCallbacks.forEach(fn => fn())
}
}
if (this.state === 'pending') {
handleReject(reason)
}
}
executor(resolve, reject)
}
then(onfulfilled, onrejected) {
let p2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
let x = onfulfilled(this.value)
resolve(x)
}
if (this.state === 'rejected') {
let x = onrejected(this.reason)
resolve(x)
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
let x = onfulfilled(this.value)
resolve(x)
})
this.onRejectedCallbacks.push(() => {
let x = onrejected(this.value)
resolve(x)
})
}
})
return p2
}
}
基本已经ok了,下面是处理一些特殊情况
在真实Promise中,面对这种情况
let p = new Promise((resolve, reject) => {
resolve(1)
})
let p2 = p.then((res) => {
console.log(res)
return p2
})
控制台显示
在我们的MyPromise中,其实这种情况还好,MyPromise里传的是一个同步函数,这样执行p.then时,其中的回调也是立即执行,此时p2还没初始化,报错信息是这样的
或者你这么写
let p2
p2 = p.then(res => {
console.log(res)
return p2
})
console.log(p2)
这样甚至不会报错,你可以打印出p2,状态是fulfilled,value是undefined罢了
但当,MyPromise中传入的是一个异步函数就会出问题了
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
let p2 = p.then((res) => {
console.log(res)
return p2
})
此时因为传入的是异步函数,onfulfilled回调并不会立即执行,那么p2就会被初始化了,就会出现问题
所以我们需要对这种情况进行捕获和抛出异常,模拟真实Promise
class MyPromise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
const handleResolve = x => {
if (x instanceof MyPromise) {
x.then(handleResolve)
} else {
this.state = 'fulfilled'
this.value = x
this.onResolvedCallbacks.forEach(fn => fn())
}
}
if (this.state === 'pending') {
handleResolve(value)
}
}
const reject = (reason) => {
const handleReject = x => {
if (x instanceof MyPromise) {
x.then(handleReject)
} else {
this.state = 'rejected'
this.reason = x
this.onRejectedCallbacks.forEach(fn => fn())
}
}
if (this.state === 'pending') {
handleReject(reason)
}
}
executor(resolve, reject)
}
then(onfulfilled, onrejected) {
let p2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
let x = onfulfilled(this.value)
// 设置定时器,保证p2被初始化再执行
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
}
if (this.state === 'rejected') {
let x = onrejected(this.reason)
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
let x = onfulfilled(this.value)
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
})
this.onRejectedCallbacks.push(() => {
let x = onrejected(this.value)
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
})
}
})
return p2
}
}
好,最后再加一点小细节
new Promise
时,传入的参数不是函数,Promise会报错
let p = new Promise(1)
- onrejected函数的返回值也能被链式调用接收,作为下一个成功回调的value
- 在Promise中reject后,没有失败回调函数,或没有catch会throw出reason
修改如下
class MyPromise {
constructor(executor) {
if (typeof executor !== 'function') throw new TypeError {
`Promise resolver ${exector} is not a function`
}
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
const handleResolve = x => {
if (x instanceof MyPromise) {
x.then(handleResolve)
} else {
this.state = 'fulfilled'
this.value = x
this.onResolvedCallbacks.forEach(fn => fn())
}
}
if (this.state === 'pending') {
handleResolve(value)
}
}
const reject = (reason) => {
const handleReject = x => {
if (x instanceof MyPromise) {
x.then(handleReject)
} else {
this.state = 'rejected'
this.reason = x
this.onRejectedCallbacks.forEach(fn => fn())
}
}
if (this.state === 'pending') {
handleReject(reason)
}
}
executor(resolve, reject)
}
then(onfulfilled, onrejected) {
let p2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
let x = onfulfilled(this.value)
// 设置定时器,保证p2被初始化再执行
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
}
if (this.state === 'rejected') {
// 同步时,无失败回调
if (!onrejected) throw this.reason
let x = onrejected(this.reason)
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
let x = onfulfilled(this.value)
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
})
this.onRejectedCallbacks.push(() => {
// 异步时,无失败回调
if (!onrejected) throw this.reason
let x = onrejected(this.value)
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
})
}
})
return p2
}
}
ok,成功实现了我们自己的Promise,虽然上面只有一个then方法
给MyPromise添加静态方法
下面将向MyPromise上添加四个静态方法
resolve
reject
race
all
resolve
传入一个value,获得包裹该value的成功状态的Promise对象
static resolve = value => {
return new MyPromise((resolve, reject) => {
resolve(value)
})
}
reject
传入一个reason,获得包裹该reason的失败状态的Promise对象
static reject = reason => {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
race
传入一个Promise数组,获得最先成功(或失败)的Promise对象
static race = promises => {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject)
})
})
}
all
传入一个Promise数组,等其中所有Promise对象状态从pending变为fulfilled时,返回一个Promise对象,该对象的value是Promise数组的所有value构成的数组(按顺序)。
若其中有Promise对象状态变为rejected,就返回一个rejected状态的Promise对象,reason与其中rejected的Promise对象相同
static all = promises => {
return new MyPromise((resolve, reject) => {
const valArr = []
// 计数器
// 因为使用valArr[x] = y的方式赋值,length不一定等于存入元素数
let count = 0
function processData(i, value) {
// 需要索引值,以保证value的顺序
valArr[i] = value
count++
if (count === promises.length) {
resolve(valArr)
}
}
promises.forEach((promise, i) => {
promise.then(value => {
// 利用闭包保存状态
processData(i, value)
}, reject) // 有rejected的promise直接reject了
})
})
}
最终的MyPromise.js
// MyPromise.js
class MyPromise {
constructor(executor) {
if (typeof executor !== 'function') throw new TypeError(
`Promise resolver ${executor} is not a function`
)
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
const handleResolve = x => {
if (x instanceof MyPromise) {
x.then(handleResolve)
} else {
this.state = 'fulfilled'
this.value = x
this.onResolvedCallbacks.forEach(fn => fn())
}
}
if (this.state === 'pending') {
handleResolve(value)
}
}
const reject = (reason) => {
const handleReject = x => {
if (x instanceof MyPromise) {
x.then(handleReject)
} else {
this.state = 'rejected'
this.reason = x
this.onRejectedCallbacks.forEach(fn => fn())
}
}
if (this.state === 'pending') {
handleReject(reason)
}
}
executor(resolve, reject)
}
then(onfulfilled, onrejected) {
let p2 = new MyPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
let x = onfulfilled(this.value)
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
}
if (this.state === 'rejected') {
if (!onrejected) throw this.reason
let x = onrejected(this.reason)
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
let x = onfulfilled(this.value)
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
})
this.onRejectedCallbacks.push(() => {
if (!onrejected) throw this.reason
let x = onrejected(this.reason)
setTimeout(() => {
if (x === p2) throw new TypeError('Chaining cycle detected for promise #<Promise>')
else resolve(x)
}, 0)
})
}
})
return p2
}
static resolve = value => {
return new MyPromise((resolve, reject) => {
resolve(value)
})
}
static reject = reason => {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
static race = promises => {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject)
})
})
}
static all = promises => {
return new MyPromise((resolve, reject) => {
const valArr = []
let count = 0
function processData(i, value) {
valArr[i] = value
count++
if (count === promises.length) {
resolve(valArr)
}
}
promises.forEach((promise, i) => {
promise.then(value => {
processData(i, value)
}, reject)
})
})
}
}
不足之处
本文对Promise的异常,处理得比较粗糙,包括promise对象rejected了,但没有onrejected函数传入也没有抛出错误…只是用了一个简单的方法,实现了then方法没有传入onrejected但promise对象rejected时会报错
肯定和真正的Promise有不少差距,但大体上我想能够帮助你更好地理解Promise