实现一个Promise
本篇文章将介绍并实现Promise的主要API,我不会详细介绍Promise的使用,适合对Promise有一定理解的同学阅读。
为了易于理解,本篇文章并没有考虑到部分比较特殊的情况。但是主要的实现会说清楚
1 起步
我们约定我们的类名为PromiseX
-
首先,我们知道Promise有三种状态,默认的状态是pending,意味等待中
const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECT = 'reject' class PromiseX { status = PENDING } -
其次,他接收一个立即执行函数,用于改变内部的状态,这个函数接收的参数分别为resolve和reject,来自于类的内部的方法
class PromiseX { constructor(executor) { executor(this.resolve,this.reject) } status = PENDING // ------- 一下是两个改变状态的内部方法 resolve() { // 由于Promise的状态只能更改一次,所以这里加了一个判断,如果状态改变过无法再次改变 if (this.status !== PENDING) return this.status = FULFILLED } reject() { if (this.status !== PENDING) return this.status = REJECT } } -
然后,Promise最关键的是他能够使用then实现链式调用,这个then也是其内部的方法,他接收两个函数,分别是成功和失败的回调。不同的是这两个函数我们需要手动实现,而上面的resolve是Promise已经帮我们定义好了
class PromiseX { //....省略上面的代码 then(success,fail) { if(this.status ===FULFILLED) { success() } else if(this.status ===REJECT) { fail() } } } -
以上就实现了一个简单的Promise框架了,不过他还有很多缺点,接下来我们一一完善
2 锦上添花
1 加入异步逻辑
-
我们上面的情况调用then 的时候只是解决了同步运行的情况下,假如说我在使用Promise 的时候使用了一个定时器,三秒后再调用resolve改变Promise的状态,但是你此时又调用了then方法,而我们此时的Promise的状态还是PENDING,所以要在then中加入一个pending的判断语句
-
实现的逻辑是如果当前的状态还是等待中,我们在内部就使用一个数组存储一个回调函数,等待resolve执行后去调用这个回调函数的数组
successFn = [] failedFn = [] // 加入异步逻辑,比如说你在一个定时器之中不能立马执行,需要把调用的成功函数的回调保存起来在修改状态的时候调用 else { this.successFn.push(success) this.failedFn.push(success) } -
我们还需要在类内部顶一个value的值,用于存储使用resolve时传入的值
value = undefined reason = undefined resolve = value => { if (this.status !== PENDING) return this.status = FULFILLED this.value = value // 这里再次调用的时候传入当前的value的值 this.successFn.forEach(fn => fn(this.value)) } -
此时的完整的代码
const PENDING = 'pending' const FULFILLED = 'fulfilled' const REJECT = 'reject' class Promisex { constructor(executor) { executor(this.resolve, this.reject) } successFn = [] failedFn = [] status = PENDING value = undefined reason = undefined resolve = value => { if (this.status !== PENDING) return this.status = FULFILLED this.value = value // 这里再次调用的时候传入当前的value的值 this.successFn.forEach(fn => fn(this.value)) } reject = reason => { if (this.status !== PENDING) return this.status = REJECT this.reason = reason this.failedFn.forEach(fn => fn(this.reason)) } then(success, fail) { if ((this.status === FULFILLED)) success(this.value) else if ((this.status === REJECT)) fail(this.reason) // 加入异步逻辑,比如说你在一个定时器之中不能立马执行,需要把调用的成功函数的回调保存起来在修改状态的时候调用 else { this.successFn.push(success) this.failedFn.push(success) } } } -
来个例子测试一下吧
下面这个例子,Promise内部的状态会在1秒之后才改变,then里面的成功回调也会在1s后执行
const p = new Promisex((resolve, reject) => { setTimeout(() => { resolve() console.log('1') }, 1000) }).then(() => console.log('2'))
2 实现链式调用
-
到现在,我们的then只能够链式调用一次,如果想要实现Promise真正的链式调用,我们可以让then返回的也是一个新的Promise,这样每个then返回的新对象都能调用里面的then方法了
then(success, fail) { return new Promisex((resolve, reject) => { if (this.status === FULFILLED) { // 如果传入的回调函数success有返回值,可以通过resolve将它它传递给当前的promise const x = success(this.value) resolve(x) } else if (this.status === REJECT) fail(this.reason) else { this.successFn.push(success) this.failedFn.push(success) } }) }
3 实现Promise.all
-
Promise.all方法使用介绍
-
他是一个静态的方法,意味着不需要实例就可以调用。所以使用static关键字声明
-
返回的是一个新的Promise,使用then方法可以得到按固定顺序结果得到的数组
-
直接看个例子吧,比较形象
// 定义两个测试函数 const fn1 = () => new Promise((resolve,reject) => { setTimeout(()=>{ resolve('11111') },2000) }) const fn2 = () => new Promise(resolve=>{ resolve('2222') }) Promise.all(['x','y',fn1(),fn2()]).then(result => console.log(result)) -
上面的结果是
['x','y','1111','2222']也就说就Promise.all方法里面不管你是不是异步方案,得到的结果都是相同的顺序
-
-
实现
-
首先我们考虑数组都是普通数据,而不是异步函数,这样我们很显然可以直接循环一遍数组,然后将结果存放到一个结果数组里面,当循环到最后一项的时候使用resolve保存这个Promise里面的数据即可
static all(array) { return new Promisex ((resolve,reject)=>{ const result = [] array.forEach((item,index) => { result.push(item) if(index===array.length) resolve(result) }) }) } -
但是,如果我们数组中如果存放的是异步函数,我们循环一遍数组就结束了,但是可能还有异步函数没执行完,所以我们记录当前这个异步函数的索引,当异步函数执行的时候推入相应索引的结果数组中,当所有索引都结束时,就可以resolve这个Promise
// 这是一个静态方法,使用static定义 static all(array) { return new Promisex((resolve, reject) => { // 结果数组 const result = [] // 完成了几项 let finish = 0 array.forEach((item, index) => { // 如果是一个promise的实例就使用then方法在他状态改变之后进行和赋值操作 if (item instanceof Promisex) { item.then( value => { result[index] = value finish++ if (finish === array.length) resolve(result) }, reason => reject(reason) ) // 如果只是一正常的值就直接放进结果数组了 } else { result[index] = item finish++ if (finish === array.length) resolve(result) } }) }) }
-
3 结语
本文章的Promise并不完整,并没有考虑到如调用then后返回的是又是Promise的情况,但是主要API的实现还是比较显而易见的,最后献上完整的代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECT = 'reject'
class Promisex {
constructor(executor) {
executor(this.resolve, this.reject)
}
// 这是一个静态方法,使用static定义
static all(array) {
return new Promisex((resolve, reject) => {
// 结果数组
const result = []
// 完成了几项
let finish = 0
array.forEach((item, index) => {
// 如果是一个promise的实例就使用then方法在他状态改变之后进行和赋值操作
if (item instanceof Promisex) {
item.then(
value => {
result[index] = value
finish++
if (finish === array.length) resolve(result)
},
reason => reject(reason)
)
// 如果只是一正常的值就直接放进结果数组了
} else {
result[index] = item
finish++
if (finish === array.length) resolve(result)
}
})
})
}
successFn = []
failedFn = []
status = PENDING
value = undefined
reason = undefined
resolve = value => {
if (this.status !== PENDING) return
this.status = FULFILLED
this.value = value
// 这里再次调用的时候传入当前的value的值
this.successFn.forEach(fn => fn(this.value))
}
reject = reason => {
if (this.status !== PENDING) return
this.status = REJECT
this.reason = reason
this.failedFn.forEach(fn => fn(this.reason))
}
then(success, fail) {
if (this.status === FULFILLED) success(this.value)
else if (this.status === REJECT) fail(this.reason)
// 加入异步逻辑,比如说你在一个定时器之中不能立马执行,需要把调用的成功函数的回调保存起来在修改状态的时候调用
else {
this.successFn.push(success)
this.failedFn.push(success)
}
}
}
\