碎碎念:
亲爱的读者:你好!我叫 Changlon —— 一个非科班程序员、一个致力于前端的开发者、一个热爱生活且又时有忧郁的思考者。
如果我的文章能给你带来一些收获,你的点赞收藏将是对我莫大的鼓励!
我的邮箱:thinker_changlon@163.com
我的Github: https://github.com/Changlon
本章我将带领大家手写一个符合promise/a+ 规范的Promise。可以说这是前端工程师必须要掌握的一个技术,在项目中使用到的非常多。 那么promsie究竟是如何实现的呢?本篇文章将通过手写一个promsie来揭开Promsie神秘的面纱! 下面请拿起你的键盘跟我一起来撸一个promise吧!相信写完之后你会对Promise有更加深刻的理解!
在开始前我想请大家先阅读一下什么是promsie/a+规范,它规定了我们如何去实现这个类。
相关链接 promise/a+规范网址 myPromise源码github地址
一 、promsie构造函数
先来写一个promise的构造函数吧,先来想一想我们平时使用Promise都是一般这样使用的 new Promise((resolve,reject)=>{}) 。我们要传一个执行函数作为构造器的参数。这个执行函数是在实例化类的时候执行的,也就是说这里面的代码是非异步执行。
export default class promise{
constructor(hanlder) {
this.checkHandler(hanlder) ?
this.init(hanlder)
: void 0
}
init(hanlder) {
}
checkHandler(hanlder) {
if(!hanlder) throw new Error('new promsie 需要一个函数类型的参数!')
if(!(hanlder instanceof Function)) throw new Error('new promsie 需要一个函数类型的参数!')
return true
}
}
上面的代码我们先判断在new promise 类时是否传入了正确的参数,如果检查参数符合要求则进入init 方法初始化类,不符合要求则抛出我们规定的异常。 接下来我们就来考虑如何去实现这个init 方法。首先我们需要记录一下实例对象,然后在实例上定义一组状态变量用来确定当前promise的状态。然后去执行这个执行函数handler 就可以了。
const that = this
that.pending = "pending"
that.fulfilled = "fulfilled"
that.rejected = 'rejected'
that.status = this.pending
that.value = undefined
function onResolved(value) {
if(that.status!=that.pending) return void 0
that.status = that.fulfilled
that.value = value
}
function onRejected(reason) {
if(that.status!=that.pending) return void 0
that.status = that.rejected
that.value = reason
}
try {
hanlder(onResolved,onRejected)
}catch(e) {
throw new Error(`用户自定义程序报错:${e}`)
}
二 、then函数
我们再来回顾一下Promise 的用法。当我们在执行函数中同步resolve,reject 值时,then函数也是同步执行的。如何resolve,reject 值是在异步代码中,那么then函数会在异步代码执行后再去执行。 举个例子:
const p = new promise((resolve,reject)=>{
setTimeout(()=>{
resolve('test')
},1000)
})
p.then(value=>{
console.log(value)
},reason=>{
console.log(reason)
})
我们发现then函数是根据 status状态来异步或同步执行的 。当状态是 'fulfilled' 或'rejected' 时候then 函数同步执行,如果是'pending' 状态则说明改变状态的函数是在下一个事件循环中才被执行,我们这时候也需要将对then中处理结果值得函数onFulfilled,onRejected 放到异步队列中去执行。 then函数的简单实现如下:
then(onFulfilled,onRejected) {
const that = this
const value = this.value
switch (that.status) {
case 'pending':
setTimeout(()=>{
that.then(onFulfilled,onRejected)
break
case 'fulfilled':
onFulfilled(value)
break
case 'rejected' :
onRejected(value)
break
}
}
三、 实现promise/a+规范的then函数
上面实现的then 函数也仅仅能满足我们上面测试用例demo1的情况,要实现一个符合promise/a+规范的then 函数我们还需要实现以下要求:
摘录自promise/a+网站 then must return a promise [3.3]. promise2 = promise1.then(onFulfilled, onRejected); If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x). If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason. If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1. If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.
总结一下就是: 1.then 函数返回一个promise 2.在onFulfilled or onRejected 中返回一个值如果这个值是非promise类型的则直接返回 'fulfilled' 状态的promise 对象,如果是promise 类型的对象则直接返回这个对象。 3.如果在onFulfilled or onRejected 用户给出的执行函数中出现报错则必须返回一个'rejected' 状态的promise 4. 如果在一个then 函数中 onFulfilled 未给出,并且状态已经为'fulfilled' 我们需要将状态值 value 作为返回的promise 的状态值。onRejected 未给出也同样如此。
下面我们来分析如何实现上面的要求。第一点很好实现我们直接修改then 函数
then(onFulfilled,onRejected) {
const that = this
const value = this.value
return new promise((resolve,reject)=>{
switch (that.status) {
case 'pending':
setTimeout(()=>{
that.then(onFulfilled,onRejected)
break
case 'fulfilled':
onFulfilled(value)
break
case 'rejected' :
onRejected(value)
break
}
})
}
可这样是不行的,返回的是一个状态永远为'pending' 的promise ,我们需要记录onFulfilled,onRejected 的返回值然后将返回值resolve 。 如下修改switch 中的代码
case 'fulfilled':
resolve(onFulfilled(value))
break
case 'rejected' :
resolve(onRejected(value))
break
可这样还是不行,如果返回的是一个promise 对象不是js基本数据类型怎么办?还有 onFulfilled ,onRejected 是使用者给出的函数,如果未传入或者在执行过程中报异常了怎么处理? 针对上面的问题我们逐一分析。 1.如果onFulfilled,onRejected 返回的是一个promise 我们怎么办,我们定义一个遍历函数去遍历这个promise状态直到最后的状态值是基本数据类型的时候我们再在返回的promise 中去resolve ,或reject 这个值。 2.onFulfilled 或onRejected 未传入怎么办? 如果未传入我们直接将它作为返回 的promise 状态值去向下传递。 3.onFulfilled 或onRejected 在执行过程中报异常怎么办? 我们直接将所有执行的代码包含在try...catch 中一旦报错则返回'rejected' 状态的promise 即可。
根据上面的分析我们最终修改代码如下:
then(onFulfilled,onRejected) {
const that = this
const value = this.value
return new promise((resolve,reject)=>{
try {
switch (that.status) {
case 'pending':
setTimeout(()=>{
const p = that.then(onFulfilled,onRejected)
p.then(value=>{resolve(value)},reason=>{reject(reason)})
})
break
case 'fulfilled':
onFulfilled?
void (()=>{
const p = onFulfilled(value)
if(!(p instanceof promise)) return resolve(p)
that.traversePromise(p,resolve,reject)
})()
: resolve(value)
break
case 'rejected' :
onRejected ?
void (()=>{
const p = onRejected(value)
if(!(p instanceof promise)) return resolve(p)
that.traversePromise(p,resolve,reject)
})()
: reject(value)
break
}
}catch(e) {
reject(e)
}
})
}
四 、实现catch捕获异常函数
catch(onRejected) {
return this.then(void 0,onRejected)
}
五 、实现 resolve,reject静态函数
static reject(p) {
return (p instanceof promise) ? p : new promise((r,j)=>{
j(p)
})
}
static resolve(p) {
return (p instanceof promise) ? p : new promise((r,j)=>{
r(p)
})
}
六、 实现 race,all函数
static race(promsies) {
if(!promsies) throw new Error('缺少promises参数!')
return new promise((resolve,reject)=>{
for(let p of promsies) {
promise.resolve(p)
.then(value=>{
resolve(value)
})
.catch(err=>{
reject(err)
})
}
})
}
static all(promsies) {
if(!promsies) throw new Error('缺少promises参数!')
return new promise((resolve,reject)=>{
const resolves = []
let counter = 0
for(let i = 0 ;i<promsies.length;++i) {
const p = promsies[i]
promise.resolve(p)
.then(value=>{
resolves[i] = value
counter++
counter === promsies.length ? resolve(resolves) : void 0
},reason=>{
reject(reason)
})
}
})
}
以上就实现了一款比较简单的promise的版本,相信通过这些代码的练习你对promise的理解已经更加深入!看到这里请给我点个赞吧!原创不易,代码都是我敲到凌晨才出来的。
|