认真看完这篇文章, 您可以自己封装一个简易但功能相对齐全的Promise, 还可以加深对Promise的理解
建议 : 看这篇文章之前希望您
- 了解ES6的语法 [ 阮一峰老师的ES6入门 ]
- 了解Promises/A+规范 [ Promises/A+ ]
- 会使用Promise
文章较长, 代码连贯性较强, 从简单开始入手, 读者可以按需选读
一. 最简单的Promise
class Promise {
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
/*
状态函数 resolve, reject
1.pending -> fulfilled, pending -> rejected
2.把数据储存到Promise实例上 this.value = value, this.reason = reason
*/
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)
}
}
}
复制代码
ps : 测试工具为vsCode的Quokka插件
根据Promise
的状态函数res
和rej
,对应执行then
中的处理函数onFulfilled
和onRejected
二. 异步的Promise
1. then()为异步
我们都知道,Promise中的then函数的代码是异步执行的,而我们写的这个并不是,可以验证一下
显然这段代码是同步执行的,而我们想要的输出顺序是 0 2 1
,所以我们可以使用setTimeout模拟这个异步
class Promise {
constructor (executor) { ... }
then (onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
/* 使用setTimeout模拟异步 */
setTimeout(() => {
onFulfilled(this.value)
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
onRejected(this.reason)
}, 0);
}
}
}
复制代码
ok, 完美得到我们想要的!
2. 状态函数异步执行
当状态函数res/rej
为异步执行时, 我们可以看到
then
是没有反应的
左边灰色小方块表明这行代码没有执行
为什么呢? 那是因为当执行到then
函数的时候,res
为异步执行,所以状态还是pending
,而我们的then
函数里面还没有对状态为pending
的处理, 修改一下代码
class Promise {
constructor (executor) {
...
/* 状态函数异步执行时, 处理函数的存储列表 */
this.resolveCallBackList = []
this.rejectCallBackList = []
const resolve = value => {
if (this.state === 'pending') {
...
/* 如果有, 则执行处理函数列表里的函数 */
this.resolveCallBackList.length > 0
&& this.resolveCallBackList.forEach(e => e())
}
}
const reject = reason => {
if (this.state === 'pending') {
...
this.rejectCallBackList.length > 0
&& this.rejectCallBackList.forEach(e => e())
}
}
...
}
then (onFulfilled, onRejected) {
...
/* 状态为pending时, 把处理函数存储对相应的列表 */
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
onFulfilled(this.value)
})
onRejected && this.rejectCallBackList.push( () => {
onRejected(this.reason)
})
}
}
}
复制代码
这样, 状态函数异步执行的时候也可以处理了, 可以简单理解为, 当状态为pending
时, 把处理函数onFulfilled/onRejected
存起来, 等状态函数res/rej
执行时, 自动执行对应的处理函数
三. Promise的错误捕捉
当发生错误时, Promise
不会报错, 而是由失败的处理函数then函数的第二个函数
捕捉错误并处理, 如果我们自己写的Promise
发生错误的话, 毫无意外是直接报错的, 就像这样
既然执行时发生错误, 那么我们就可以使用try/catch
去捕获错误
class Promise {
constructor (executor) {
...
/* 使用try/catch捕获错误, 并执行reject, 改变状态为rejected */
try {
executor(resolve, reject)
} catch (error) {
this.state === 'pending' && reject(error)
}
}
then (onFulfilled, onRejected) { ... }
}
复制代码
四. then函数详解
then函数有两个特性
- then函数执行完返回一个新的Promise实例
- then函数能链式调用
1. then的链式调用
new Promise(res => res(0))
.then(value => {
console.log(value) // 0
return `1 fulfilled`
})
.then(value => {
console.log(value) // 1 fulfilled
})
复制代码
then
函数执行后返回一个Promise
实例, 该Promise
实例的状态由then
决定, 下一个then
函数根据返回的这个Promise
实例执行相应的处理函数, 画个图
then
的执行依赖于上一个
then
执行返回的
Promise
实例, 而这个
Promise
实例的数据由上一个
then
的处理函数
onFulfilled/onRejected
的
执行和其返回值
决定
2.then的处理函数返回值不是一个Promise实例
如果按照字面意思去写代码
class Promise {
constructor (executor) { ... }
then (onFulfilled, onRejected) {
/* 一个新的Promise实例 */
const newPromise = new Promise ( (res, rej) => {})
...
return newPromise
}
}
复制代码
如果这样写, 是没意义的, 返回的Promise
实例的状态永远为pending
, 因为没有执行状态函数res/rej
, 因此也无法进行then
函数的链式调用
因为new Promise(executor)
的executor
函数是同步执行的, 所以我们可以这样写
class Promise {
constructor (executor) { ... }
then (onFulfilled, onRejected) {
const newPromise = new Promise ( (res, rej) => {
/*
这部分的处理函数是同步执行的, 因此可以放在里面执行
同时还能通过res/rej改变返回的Promise实例的状态
*/
if (this.state === 'fulfilled') {
setTimeout(() => {
/* 拿到处理函数执行后的返回值 */
const value = onFulfilled(this.value)
/* 改变返回的Promise实例的状态并把数据传过去 */
res(value)
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
const reason = onRejected(this.reason)
res(reason)
}, 0);
}
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
const value = onFulfilled(this.value)
res(value)
})
onRejected && this.rejectCallBackList.push( () => {
const reason = onRejected(this.reason)
res(reason)
})
}
})
return newPromise
}
}
复制代码
哒哒,
then
的链式调用完成了
ps : then
的处理函数返回值不是一个Promise
实例时, 无论fullfilled
还是rejected
, 都是执行下一个then
函数的onFulfilled
3.then的处理函数返回值是一个Promise实例
当then
的处理函数返回值是一个Promise
实例时, 则下一个then
函数的执行, 全部由这个Promise
实例决定, 所以我们需要使用checkReturnValueIfPromise
函数去判断一下返回值的类型并处理对应的情况
class Promise {
constructor (executor) { ... }
/*
promise -> Promise对象
target -> then的处理函数的返回值
res/rej -> 要返回的Promise实例的状态函数
*/
checkReturnValueIfPromise (promise, target, res, rej) {
if (target instanceof promise) {
/*
如果是Promise实例
则调用then函数,根据Promise实例的状态执行对应的处理函数
从而改变要返回的Promise实例的状态
如果下面的代码不能理解, 也可以写成这样
target.then( value => {
res(value)
}, reason => {
rej(reason)
} )
*/
target.then(res, rej)
} else {
res(target)
}
}
then (onFulfilled, onRejected) {
const newPromise = new Promise ( (res, rej) => {
if (this.state === 'fulfilled') {
setTimeout(() => {
const value = onFulfilled(this.value)
/* 调用检测函数并做相关处理 */
this.checkReturnValueIfPromise(Promise, value, res, rej)
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
const reason = onRejected(this.reason)
this.checkReturnValueIfPromise(Promise, reason, res, rej)
}, 0);
}
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
const value = onFulfilled(this.value)
this.checkReturnValueIfPromise(Promise, value, res, rej)
})
onRejected && this.rejectCallBackList.push( () => {
const reason = onRejected(this.reason)
this.checkReturnValueIfPromise(Promise, reason, res, rej)
})
}
})
return newPromise
}
}
复制代码
就算是异步也是一点毛病都没有
五. 一些Promise上的方法 (直接上代码)
对了, 还有一个与then
类似的方法catch
, 这个方法是专门处理rejected
状态的, 代码也就只有一句话
class Promise {
constructor () { ... }
then () { ... }
catch (onRejected) {
this.then(undefined, onRejected)
}
}
复制代码
1. Promise.resolve
返回一个fulfilled
状态的Promise
实例
class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve (value) {
return new Promise( res => res(value))
}
}
复制代码
2. Promise.reject
返回一个rejected
状态的Promise
实例
class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve () { ... }
static reject (reason) {
return new Promise( (undefined, rej) => rej(reason))
}
}
复制代码
3. Promise.race
接收一个Promise
实例的数组promiseArray
, 返回一个Promise
实例, 返回的Promise
实例由promiseArray
中执行最快的Promise
实例决定
class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve () { ... }
static reject () { ... }
static race (promiseArray) {
return new Promise ( (res, rej) => {
promiseArray.forEach( promise => {
promise.then(res, rej)
})
})
}
}
复制代码
4. Promise.all
功能描述太长了, 不懂的可以去看 阮一峰老师对于Promise.all的介绍
class Promise {
constructor () { ... }
then () { ... }
catch () { ... }
static resolve () { ... }
static reject () { ... }
static race () { ... }
static all (promiseArray) {
let count = 0,
resultArray = []
return new Promise( (res, rej) => {
promiseArray.forEach( promise => {
promise.then( value => {
count++
resultArray.push(value)
if (count === promiseArray.length) {
res(resultArray)
}
}, reason => {
rej(reason)
})
})
})
}
}
复制代码
六. 完整的代码 (带注解)
上面的代码也不是完美的, 还有一些细枝末节的问题没解决, 不过也完成了核心的功能, 下面给出稍微完整的代码(带注解)
class Promise {
constructor (executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} must be a function`)
}
this.state = 'pending'
this.value = undefined
this.reason = undefined
/* 异步时, then()函数的执行函数(onFulfilled, onRejected)的存储列表 */
this.resolveCallBackList = []
this.rejectCallBackList = []
/**
* @method resolve
* @param {string} value 成功时的参数
* @function 改变状态, 传递参数, 遍历成功异步缓存函数列表
* @returns {undefined}
*/
const resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.resolveCallBackList.length > 0 && this.resolveCallBackList.forEach(e => e())
}
}
/**
* @method reject
* @param {string} reason 失败时的参数
* @function 改变状态, 传递参数, 遍历失败异步缓存函数列表
* @returns {undefined}
*/
const reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
this.rejectCallBackList.length > 0 && this.rejectCallBackList.forEach(e => e())
}
}
/* 实例 Promise过程发生错误, 则直接把状态改为 rejected */
try {
executor(resolve, reject)
} catch (error) {
this.state === 'pending' && reject(error)
}
}
/**
* @method checkLastThenReturn
* @param {Promise} promise Promise对象
* @param {*} target then处理函数(onFulfilled/onRejected)的返回值
* @param {function} res 下一个Promise实例的成功状态转换函数
* @param {function} rej 下一个Promise实例的失败状态转换函数
* @function 判断then()的处理函数的返回值是否为新的Promise实例
*/
checkLastThenReturn (promise, target, res, rej) {
if (target instanceof promise) {
/* 如果target是一个新的 Promise实例,则根据该实例的执行,改变下一个Promise实例的状态 */
target.then(res, rej)
} else {
/* 如果不是, 则直接执行成功的状态改变函数 */
res(target)
}
}
/**
* @method then
* @param {function} onFulfilled 状态为fulfilled时执行
* @param {function} onRejected 状态为rejected时执行
* @function 返回一个 Promise实例, 该Promise实例的状态,由该 then()的执行情况决定
* @returns {Promise} 返回一个新的Promise对象
*/
then (onFulfilled, onRejected) {
/* 处理 then()不传参数且上一个操作为异步时, 直接返回上一个实例 */
if (!onFulfilled && !onRejected && this.state === 'pending') return this
/* 处理 then(onFulfilled)只有成功处理函数但状态为 rejected的情况, 直接返回一个失败的Promise实例 */
if (!onRejected && this.state === 'rejected') return Promise.reject(this.reason)
/* 处理 then()不传参的问题,直接把处理函数重置为返回接收的参数 */
if (!onFulfilled && !onRejected) {
onFulfilled = value => value
onRejected = reason => reason
}
/**
* @method returnPromise
* @param {function} res 使返回的Promise对象状态转为 fulfilled
* @param {function} rej 使返回的Promise对象状态转为 rejected
* @function 同步执行,根据then()的执行情况,执行res还是 rej,改变该Promise实例的状态并执行异步存储函数列表
*/
const returnPromise = new Promise( (res, rej) => {
/* then()的处理函数同步执行 */
if (this.state === 'fulfilled') {
/* 使用setTimeout模拟 then()里面的内容异步执行 */
setTimeout(() => {
/* 如果处理函数出错,则返回的 Promise实例为 rejected */
try {
const value = onFulfilled(this.value)
this.checkLastThenReturn(Promise, value, res, rej)
} catch (error) {
rej(error)
}
}, 0);
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
const reason = onRejected(this.reason)
this.checkLastThenReturn(Promise, reason, res, rej)
} catch (error) {
rej(error)
}
}, 0);
}
/* then()处理函数异步执行 */
if (this.state === 'pending') {
onFulfilled && this.resolveCallBackList.push( () => {
try {
const value = onFulfilled(this.value)
this.checkLastThenReturn(Promise, value, res, rej)
} catch (error) {
rej(error)
}
})
if (onRejected) {
this.rejectCallBackList.push( () => {
try {
const reason = onRejected(this.reason)
this.checkLastThenReturn(Promise, reason, res, rej)
} catch (error) {
rej(error)
}
})
} else {
/* 异步时没有onRejected, 直接把下一个Promise对象变为失败状态 */
this.rejectCallBackList.push( () => {
// Promise.reject(this.reason).catch(rej)
rej(this.reason)
})
}
}
})
return returnPromise
}
/**
* @method catch
* @param {function} onRejected 失败时的处理函数
* @function 没有onFulfilled的 then()
* @returns {Promise}
*/
catch (onRejected) {
this.then(undefined, onRejected)
}
/**
* @method reject
* @param {string} reason
* @function 返回一个失败状态的Promise实例
* @returns {new Promise}
*/
static reject (reason) {
return new Promise( (undefined, rej) => rej(reason))
}
/**
* @method resolve
* @param {string} value
* @function 返回一个成功状态的Promise实例
* @returns {new Promise}
*/
static resolve (value) {
return new Promise( res => res(value))
}
/**
* @method race
* @param {array} promiseArray Promise实例的数组
* @function 找出Promise实例数组中执行最快的 Promise实例
* @returns 返回一个Promise实例,该实例的状态由 Promise实例数组中执行最快的一个 Promise实例决定
*/
static race (promiseArray) {
return new Promise ( (res, rej) => {
promiseArray.forEach( promise => {
promise.then(res, rej)
})
})
}
/**
* @method all
* @param {array} promiseArray Promise实例的数组
* @function 等Promise实例数组的每个 Promise实例都执行完再把所有执行的结果放进一个数组返回,如果出错,则终止并返回出错那个Promise的数据
* @returns 返回一个 Promise实例
*/
static all (promiseArray) {
let count = 0,
resultArray = []
return new Promise( (res, rej) => {
promiseArray.forEach( promise => {
promise.then( value => {
count++
resultArray.push(value)
if (count === promiseArray.length) {
res(resultArray)
}
}, reason => {
rej(reason)
})
})
})
}
}
复制代码
七. 结语
谢谢浏览我的文章, 希望你能学到东西