题目:手写A+ Promise
一、前言
在手写promise之前我们先来了解一下什么是promiseA+ 规范,附上链接:Promise A+规范,我简单根据这个A+规范总结一下,包括本次手写Promise也是简单实现一下部分的需求;
(1):如果用面向对象的角度考虑,Promise是一个类,所有实例拥有三个属性,而且应该属于是私有属性
(2):三个属性分别是state(保存着当前promise的状态),状态的取值必须是pending (进行中), fullfilled (成功), rejected(失败)三种。
(3):每创建一个promise都必须传递一个回调函数,回调函数执行时(同步执行的),接受两个参数resolve,reject,并且这两个回调是异步执行的。
(4):实例都可以调用.then方法,这个方法接受两个参数成功的回调失败的回调,如果当前实例成功则会执行成功的回调,并把成功的参数传递给这个回调,失败则是同理,这个.then的回调函数也是异步执行。
(5):返回的promise的状态取决于回调函数中返回的值,如果返回的不是一个promise的实例,那么返回的promise属于成功的状态,值为回调的返回值,如果是一个promise,那么返回的promise的状态取决于这个Promise的状态。并保持一致。
(6):如果抛出错误那么会进入异常穿透穿透环节,只要在任意回调中抛出了错误,那么promise会拦截这个错误,无论调用多少次.then最终返回的依然是个失败的promise,并且值为抛出的异常
小结:这个是我总结的promiseA+规范中的部分规范,当然并不完全,但姑且谅解我们先从简单的开始好吗?
接下来我们来一起实现一下吧
- 先创建一个类也就是专门的promise类,我们使用ES6的新语法 class , 当然现在也已经不能算新语法了哈。
const Pending = 'pending'
const Fullfilled = 'fullfilled'
const Rejected = 'rejected'
class Promise {
constructor(executor) {
this.result = ''
this.state = Pending
// ? ?
executor(resolve, reject)
}
}
已经创建好了一个类,并且拥有自己的状态和结果值,而且当执行器函数传过来的时候要求要同步调用,因此我们直接调用它,我们需要在构造函数中定义一下那两个回调函数,这里需要注意,这个回调函数必须是异步的,而且我们需要准备一个容器数组来存储.then传入的回调函数。(这里的原因我们稍后会讲到!)
const Pending = 'pending'
const Fullfilled = 'fullfilled'
const Rejected = 'rejected'
class Promise {
constructor(executor) {
this.result = ''
this.state = Pending
// 容器数组
this.callbacks = []
const _this = this
const resolve = (result) => {
this.result = result
this.state = Fullfilled
setTimeout(() => {
_this.callbacks.forEach(cb => {
cb.onResolved && cb.onResolved(result)
})
})
}
const reject = result => {
this.result = result
this.state = Rejected
setTimeout(() => {
_this.callbacks.forEach(cb => {
cb.onRejected && cb.onRejected(result)
})
})
}
executor(resolve, reject)
}
}
回调函数属于异步调用的因此我们使用定时器来模拟,其次如果回调容器数组callbacks有回调函数的话,还将其一一进行执行,在定义这两个回调是,我们首要的任务应该是改变当前promise的状态,如果成功的话将这个promise的状态改为fullfilled,如果失败的话将这个promise的状态改为rejected
接下来要做的事情就是实现then方法了,这个方法算是promise的核心了,只要实现了promise的.then方法,那么其他的方法比如.catch ,.all ,.race等等就容易多了,接下来我们就一起来实现一下。
class Promise {
constructor(executor) {
this.result = ''
this.state = Pending
// 容器数组
this.callbacks = []
const _this = this
const resolve = (result) => {
this.result = result
this.state = Fullfilled
setTimeout(() => {
_this.callbacks.forEach(cb => {
cb.onResolved && cb.onResolved(result)
})
})
}
const reject = result => {
this.result = result
this.state = Rejected
setTimeout(() => {
_this.callbacks.forEach(cb => {
cb.onRejected && cb.onRejected(result)
})
})
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then (onResolved, onRejected) {
const _this = this
return new Promise((resolve, reject) => {
const callback = cb => {
setTimeout(() => {
try {
let result = cb(_this.result)
if (!(result instanceof Promise)) {
resolve(result)
} else {
result.then(res => {
resolve(res)
}, reason => {
reject(reason)
})
}
} catch (error) {
reject(error)
}
})
}
if (_this.state === Pending) {
// 还在pending当中
_this.callbacks.push({
onResolved,
onRejected
})
} else if (_this.state === Fullfilled) {
callback(onResolved)
} else if (_this.state === Rejected) {
callback(onRejected)
}
})
}
}
实现promise的then方法主要有几个核心,第一就是要返回一个新的Promise,所以我们二话不说先 return new Promise(( resolve,reject)=>{}) 先返回一个新的Promise,至于这个promise到底是什么样的状态呢,我们要进行判断
- 首先我们先判断一下当前promise是什么样的状态,有三种情况,首先如果是进行中的话(也就是在我们调用.then的时候上一个promise的状态还没有改变,也就是先指定回调后进行执行的情况),这个时候我们也不能立即对promise指定结果,我们可以将回调添加到callbacks当中,当上一个promise有结果的时候再进行一个调用就可以了(但是这个地方其实还有个坑稍后会讲到)。
- 其次,当前Promise如果要是已经是fullfilled状态的话,我们就要执行成功的回调,因为返回的Promise的状态由当前的回调返回值决定,因此我们可以获取返回的结果,再判断一下是不是promise的实例,如果是的话就再讲这个promise进行.then处理获取状态,如果不是的话就直接当当前的promise置为一个成功的状态。
- 在这里需要注意的是,第一步我们在当前promise为pending状态时,直接添加回调即可,这种做法肯定是不对的,因此就算回调后面执行了这个回调,当时并没有更改当前返回的这个promise的状态呀,所以我们需要扩展回调的功能,不仅需要执行回调而且还需要改变返回的promise的状态,我们急需将代码完成以下。
const Pending = 'pending'
const Fullfilled = 'fullfilled'
const Rejected = 'rejected'
class Promise {
constructor(executor) {
this.result = ''
this.state = Pending
// 容器数组
this.callbacks = []
const _this = this
const resolve = (result) => {
this.result = result
this.state = Fullfilled
setTimeout(() => {
_this.callbacks.forEach(cb => {
cb.onResolved && cb.onResolved(result)
})
})
}
const reject = result => {
this.result = result
this.state = Rejected
setTimeout(() => {
_this.callbacks.forEach(cb => {
cb.onRejected && cb.onRejected(result)
})
})
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then (onResolved, onRejected) {
const _this = this
return new Promise((resolve, reject) => {
const callback = cb => {
setTimeout(() => {
try {
let result = cb(_this.result)
if (!(result instanceof Promise)) {
resolve(result)
} else {
result.then(res => {
resolve(res)
}, reason => {
reject(reason)
})
}
} catch (error) {
reject(error)
}
})
}
if (_this.state === Pending) {
// 还在pending当中
_this.callbacks.push({
onResolved: () => {
callback(onResolved)
},
onRejected: () => {
callback(onRjected)
}
})
} else if (_this.state === Fullfilled) {
callback(onResolved)
} else if (_this.state === Rejected) {
callback(onRejected)
}
})
}
}
好了这就是目前的较为完整的版本了,大家可以自行试一下,当然catch方法,和race方法也可以自行实现一下,我在这里就直接把代码奉上啦!
const Pending = 'pending'
const Fullfilled = 'fullfilled'
const Rejected = 'rejected'
class Promise {
constructor(executor) {
this.result = ''
this.state = Pending
// 容器数组
this.callbacks = []
const _this = this
const resolve = (result) => {
this.result = result
this.state = Fullfilled
setTimeout(() => {
_this.callbacks.forEach(cb => {
cb.onResolved && cb.onResolved(result)
})
})
}
const reject = result => {
this.result = result
this.state = Rejected
setTimeout(() => {
_this.callbacks.forEach(cb => {
cb.onRejected && cb.onRejected(result)
})
})
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then (onResolved, onRejected) {
const _this = this
return new Promise((resolve, reject) => {
const callback = cb => {
setTimeout(() => {
try {
let result = cb && cb(_this.result)
if (!(result instanceof Promise)) {
resolve(result)
} else {
result.then(res => {
resolve(res)
}, reason => {
reject(reason)
})
}
} catch (error) {
reject(error)
}
})
}
if (_this.state === Pending) {
// 还在pending当中
_this.callbacks.push({
onResolved: () => {
callback(onResolved)
},
onRejected: () => {
callback(onRjected)
}
})
} else if (_this.state === Fullfilled) {
callback(onResolved)
} else if (_this.state === Rejected) {
callback(onRejected)
}
})
}
catch (onRejected) {
return this.then(null, onRejected)
}
static race (promises) {
// 这是一个数组,数组中有多个promise
if (!Array.isArray(promises)) {
return console.warn('参数不是可迭代的对象')
}
return new Promise((resolve, reject) => {
promises.forEach(promise => {
if (promise instanceof Promise) {
promise.then(res => {
resolve(res)
}, reason => {
reject(reason)
})
} else {
resolve(promise)
}
})
})
}
static all (promises) {
if (!Array.isArray(promises)) {
return console.warn('参数不是可迭代的对象')
}
return new Promise((resolve, reject) => {
let resArr = []
let index = 0
let length = promises.length
promises.forEach(promise => {
if (promise instanceof Promise) {
promise.then(res => {
resArr.push(res)
index++
if (index == length) {
resolve(resArr)
}
}, reason => {
reject(reason)
})
} else {
resArr.push(promise)
index++
if (index == length) {
resolve(resArr)
}
}
})
})
}
static resolve (value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
}
后记
本文还是有不严谨和疏忽的地方,希望有读者阅读并欢迎批评指正,我一定第一时间更改,成为一名优秀的前端工程师,我们一直在路上,加油!