1. 前言
接上文。上文已经实现了基础版的Promise,但是Promise还有几个常用的方法,如:finally、catch。这篇文章具体讲一下这两个方法的实现。
2. 实现
2.1 认识catch
catch()方法是.then(null, rejection) 或 .then(undefined, rejection)方法的别名,用于指定发生错误时的回调函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
const p = new Promise((resolve, reject) => {
console.log('立即执行')
setTimeout(() => {
reject('发生错误')
}, 2000)
})
// p.then((res) => {
// console.log('res=========', res)
// }, (err) => {
// console.log('err==========', err)
// })
// 等价于上面,但是更推荐这种写法
p.then((res1) => {
console.log('res1========', res1)
}).catch((err1) => {
console.log('err1==========', err1)
})
</script>
</head>
<body>
</body>
</html>
输出如下:
更推荐用catch()捕获错误而不是给then()方法传第二个回调函数。因为catch会捕获前面then()方法中执行的错误。catch方法返回的还是一个Promise对象。
更详细的catch教程请参考:catch
2.2 认识finally
finally方法用于指定不管Promise对象的状态如何,都会执行的操作。该方法是ES2018引入的标准。finally() 方法的回调函数不接受任何参数,这意味着没有办法知道,前面的Promise对象的状态是fulfilled还是rejected。这表明,finally()方法里面的操作,应该是与状态无关的,不依赖于Promise的执行结果。
finally()本质上还是then()方法的特例,其返回的还是一个Promsie对象。更详细的finally()方法的介绍参见:finally
2.3 实现catch和finally
基于上一篇文章的代码修改,最终代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
class MyPromise {
static Pending = 'pending'
static Fulfiled = 'fulfiled'
static Reject = 'reject'
constructor(executor) {
this.status = MyPromise.Pending // Promise实例的状态,初始化为pending
this.result = undefined // result保存异步操作成功时的返回值
this.error = undefined // error保存异步操作失败时的返回值
this.callbacks = [] // 保存then方法传入的回调函数,在实例状态改变时调用
// 执行传入的函数,为入参resolve和reject绑定this
console.log('执行传入的函数')
executor(this._resolve.bind(this), this._reject.bind(this))
}
// 实现resolve函数
_resolve(result) {
if (result instanceof MyPromise) {
// 表示onSuccess返回结果是一个Promise实例,假定这个实例为a
// 我们是想获取这个叫a的Promise实例里面的result,但是a实例并没有暴露任何获取result的方法
// 但是a实例的then方法里有个成功的回调函数,这个回调函数里可以取到result。
result.then(
this._resolve.bind(this),
this._reject.bind(this)
)
return
}
this.status = MyPromise.Fulfiled
this.result = result
this.callbacks.forEach((cb) => {
this._handle(cb)
})
}
// 实现reject函数
_reject(error) {
if (error instanceof MyPromise) {
error.then(
this._resolve.bind(this),
this._reject.bind(this)
)
}
this.status = MyPromise.Reject
this.error = error
this.callbacks.forEach((cb) => {
this._handle(cb)
})
}
// then方法把传入的回调函数保存起来,当状态改变时调用。
then(onSuccess, onError) {
// 把下一个Promise实例的resolve方法也存进callbasks中去
// 是为了把当前onSuccess的执行结果通过resolve方法赋给下一个Promise实例的result
return new MyPromise((nextResolve, nextReject) => {
this._handle({
nextResolve,
nextReject,
onSuccess,
onError
})
})
}
catch(onCatch) {
return this.then(undefined, onCatch)
}
finally(onFinally) {
return this.then(onFinally, onFinally)
}
_handle(cb) {
const {nextResolve, nextReject, onSuccess, onError } = cb
if (this.status === MyPromise.Pending) {
this.callbacks.push(cb)
}
if (this.status === MyPromise.Fulfiled) {
const nextResult = onSuccess ? onSuccess(this.result) : undefined
nextResolve(nextResult)
return
}
if (this.status === MyPromise.Reject) {
// 注意这里修改了
const nextError = onError ? onError(this.error) : this.error
nextReject(nextError)
return
}
}
}
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('Hello Promise')
}, 1000)
})
// 调用第一个then方法之后,会创建下一个Promise对象。
// 会把当前Promise对象的成功回调函数、失败回调函数、下一个Resolve方法、下一个Reject方法存起来。
// 然后在调用onSuccess之后,把其返回值作为nextResolve的参数,赋给下一个Promise对象的result1,
// 这样第二个then方法的成功回调函数就会取到result1,以此类推。
p.then((res) => {
console.log('res==========', res)
}).catch((err) => {
console.log('err==========', err)
}).finally(() => {
console.log('finally')
})
</script>
</head>
<body>
</body>
</html>
执行结果:
好了,进阶版的Promise已经实现了。该Promise仍有很多问题,此文章仅作为入门认识Promise工作及实现原理。欢迎大家批评改正。
上一篇:基础版Promise