实现promise
Promise对象用于表示一个异步操作的最终完成(或失败)及其结果值。
分析promise功能
// 标准用法
new Promise((resolve, reject) => {
if () {
resolve()
return
}
reject()
})
// 全部成功
Promise.all([promise1, promise2, promise3]).then(res => {
const [res1, res2, res3] = res
})
// 有一个执行成功
Promise.any([promise1, promise2, promise3]).then(res => {
const [res1, res2, res3] = res
})
// 第一个成功或者失败
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster
})
可以看到该函数需要实现这些功能:
- 链式调用,
.then之后还可以调用.then或者.catch - 需要我们告诉
promise当前执行后是成功还是失败,然后去链式调用函数,否则promise一直会处于pending状态 - 同时调用多个请求后的处理,根据
all,any,rece方法
实现
分步实现:
promise内部需要修改状态- 需要实现实现
then和catch方法 - 需要实现
all,any,rece方法
内部状态维护
一个 Promise 必然处于以下几种状态之一:
- 待定(pending):初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled):意味着操作成功完成。
- 已拒绝(rejected):意味着操作失败。
初始状态:
// 状态列表
const STATUS_OBJ = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected',
}
// 初始状态
let status = STATUS_OBJ.PENDING
已知resolve和reject是传入promise的参数,成功会调用resolve,失败则调用reject。可以推理得到resolve和reject是promise内部去修改状态的方法:
// 状态列表
const STATUS_OBJ = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected',
}
// 初始状态
let status = STATUS_OBJ.PENDING
function resolve () {
status = STATUS_OBJ.FULFILLED
}
function reject () {
status = STATUS_OBJ.REJECTED
}
实现then和catch
promise的handle方法中的resolve和reject会设定执行后得到的结果,then和catch在执行的时候会得到该结果传入给对应的cb:
...
let params = undefined
function resolve (res) {
params = res
status = STATUS_OBJ.FULFILLED
}
function reject (e) {
params = e
status = STATUS_OBJ.REJECTED
}
然后再实现then和catch:
function then (cb) {
cb(params)
}
function catch (cb) {
cb(params)
}
到这里,完整的函数如下:
function PromiseFn (handler) {
// 状态列表
const STATUS_OBJ = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected',
}
// 初始状态
let status = STATUS_OBJ.PENDING
let params = undefined
function resolve (res) {
params = res
status = STATUS_OBJ.FULFILLED
}
function reject (e) {
params = e
status = STATUS_OBJ.REJECTED
}
function thenFn (cb) {
cb(params)
}
function catchFn (cb) {
cb(params)
}
handler(resolve, reject)
return {
then: thenFn,
catch: catchFn
}
}
执行一下是成功的:
new PromiseFn((resolve) => {
resolve('10086')
}).then(res => {
console.log(res) // 打印结果 10086
})
但是到这里存在几个问题:
- 当
resolve是在请求后再去执行的,那按现在写法.then执行的时候是拿不到数据的,并且promise的写法,.then执行后依旧可以执行.then或者.catch等promise上的各种方法 then和catch不能同时执行- 当传入
promise的函数执行错误要怎么处理 - 当
resolve在异步中执行的时候,.then如何拿到异步返回的数据
第一个问题,可以将方法挂载到this上并且返回this:
...
this.then = function (cb) {
cb(params)
return this
}
this.catch = function (cb) {
cb(params)
return this
}
handler(resolve, reject)
return this
第二个问题,需要对then和catch执行时机处理:
...
this.then = function (cb) {
if (status !== STATUS_OBJ.FULFILLED) return this
cb(params)
return this
}
this.catch = function (cb) {
if (status !== STATUS_OBJ.REJECTED) return this
cb(params)
return this
}
第三个问题,需要对handle执行做异常处理:
...
this.then = function (cb) {
cb(params)
return this
}
this.catch = function (cb) {
cb(params)
return this
}
try {
handler(resolve, reject)
} catch (e) {
this.catch = function (cb) {
cb(e)
return this
}
}
return this
第四个问题,当resolve或者reject是异步处理的,那应该在status改变后再去执行then或者catch方法,可以先将对应方法存储下来,等到状态改变后再去执行:
...
// 成功执行函数
let thenFn = undefined
// 失败执行函数
let catchFn = undefined
...
function resolve (res) {
params = res
status = STATUS_OBJ.FULFILLED
if (thenFn) thenFn()
}
function reject (e) {
params = e
status = STATUS_OBJ.REJECTED
if (catchFn) catchFn()
}
this.then = function (cb) {
const fn = () => {
if (status !== STATUS_OBJ.FULFILLED) return this
cb(params)
return this
}
// 状态未变化则存储当前要执行的函数
if (status === STATUS_OBJ.PENDING) {
thenFn = fn
return this
}
return fn()
}
this.catch = function (cb) {
const fn = () => {
if (status !== STATUS_OBJ.REJECTED) return this
cb(params)
return this
}
// 状态未变化则存储当前要执行的函数
if (status === STATUS_OBJ.PENDING) {
catchFn = fn
return this
}
return fn()
}
...
完整代码如下:
function PromiseFn (handler) {
// 状态列表
const STATUS_OBJ = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected',
}
// 初始状态
let status = STATUS_OBJ.PENDING
// promise结果
let params = undefined
// 成功执行函数
let thenFn = undefined
// 失败执行函数
let catchFn = undefined
function resolve (res) {
params = res
status = STATUS_OBJ.FULFILLED
if (thenFn) thenFn()
}
function reject (e) {
params = e
status = STATUS_OBJ.REJECTED
if (catchFn) catchFn()
}
this.then = function (cb) {
const fn = () => {
if (status !== STATUS_OBJ.FULFILLED) return this
cb(params)
return this
}
if (status === STATUS_OBJ.PENDING) {
thenFn = fn
return this
}
return fn()
}
this.catch = function (cb) {
const fn = () => {
if (status !== STATUS_OBJ.REJECTED) return this
cb(params)
return this
}
if (status === STATUS_OBJ.PENDING) {
catchFn = fn
return this
}
return fn()
}
try {
handler(resolve, reject)
} catch (e) {
this.catch = function (cb) {
cb(e)
return this
}
}
return this
}
执行效果:
new PromiseFn((resolve, reject) => {
setTimeout(() => {
resolve(110)
}, 2000)
}).then(res => {
console.log(res) // 2s后打印 110
}).catch(e => {
console.log('e', e)
})
2后打印 e 110:
new PromiseFn((resolve, reject) => {
setTimeout(() => {
reject(110)
}, 2000)
}).then(res => {
console.log(res) // 2s后打印 110
}).catch(e => {
console.log('e', e)
})
当前执行错误也打印e 110:
new PromiseFn((resolve, reject) => {
throw(110)
}).then(res => {
console.log(res)
}).catch(e => {
console.log('e', e)
})
至此,promise的基本功能就已经实现了,不过一个标准的promise需要遵循promiseA+规范,才能避免不必要的问题
实现all, any, rece
做之前就是分析功能,all是同时调用多个promise,全部成功后将执行的结果放入数组中返回,所以需要考虑维护一个promise状态,思路是这样:维护一组数据,分别执行promise并且通过.then方法得到所有promise的数据然后组装成一个可以返回的数据结构。
简易代码如下下:
this.all = function (arr) {
const fnList = arr.map(item => {
return {
promise: item,
status: STATUS_OBJ.PENDING,
params: undefined
}
})
}
而any和rece也是这个思路,都是基于promise的实现后去提供的一些方便使用的方法,都可以通过该方法实现了什么去反推代码实现。