本文已参与「新人创作礼」活动,一起开启掘金创作之路
promise函数自定义封装详解(三)
大家好,我们在上篇文章当中完成了异步任务的成功执行,promise构造函数优化,then方法返回值完善。那在这篇文章当中,我们还将持续的优化then方法里的一些代码逻辑和组成架构,除此之外,我们还需要在promise构造函数上添加resolve和reject的方法,因此这节课的主要任务如下
目标任务
- 完善then方法的内部代码
- 封装common函数,提高代码的可读性
- 在promise构造函数上挂载一个resolve和reject方法
完善then方法
思考
之前我们在script的then方法调用里执行相关函数时,基本都是console.log(...),简单的打印,而我们必须知道之前声明的res结果值和状态是受then方法调用时内部的方法执行的结果决定的。换句话说,then方法里的函数体中可能会有各种各样的代码,但总的来说有两种,一种是promise类型对象,一种是非promise类型对象(例如undefined,null,console.log等一些字符串),都是能够影响res结果的因素。那这又该怎么理解呢?
内置promise函数效果
我们利用JS原来内置的promise函数执行一些操作
// 创建一个promise实例化对象
let p = new Promise((resolve, reject) => {
resolve('成功')
})
// 调用promise的then方法
let res = p.then(value => {
return new Promise((resolve, reject) => {
reject('失败了哦')
})
}, reason=>{
console.log(reason)
})
console.log(res);
来看res的打印结果:
可以看到这时候res的执行结果中状态为rejected,结果值也发生了改变,这就充分说明res的值是受then方法的返回值影响的。
自定义js文件函数效果
与此同时,再来看看我们引入创建的js文件之后的打印效果:
这时打印的res的值都为默认值,与原生promise的打印结果不一样,那我们该如何使其保持一致呢? 我们来分析为什么会出现这种情况,首先我们把then里形参为value的函数传到js文件中的then方法里时,此时onResolved就是这个函数,而这时候的实例p的状态为"fulfilled",所以我们紧接着我们执行if里的语句
此时这个onResolve函数语句已经执行,再强调一遍,后面括号里的参数并不一定参与执行。可以看到这个函数执行的结果为返回一个新的promise对象,而这个对象身上已经拥有了自己的状态值和结果值,因为内部我们设置了rejected
思路与代码
那此时回到js模块中刚才图中的位置,现在来想想,如果要让res的值被then方法的返回值影响,那我们是不是该对这个返回值的类型进行判断呢?答案是肯定的
因此我们可以设置一个变量来接收这个onResolve的执行结果,再对这个result的类型进行判断,如果它为非promise类型,则我们直接让它成功执行,就不再执行后续的代码。如果此时result的类型又为promise对象,那我们就要进行相同的操作,调用then方法,再次对这个对象进行处理,整个过程可以利用try...catch...来进行捕捉可能存在的状态异常,具体代码如下
状态为成功的代码
return new Promise((resolve, reject) => {
// 成功时的then方法的调用
if (this.PromiseState === 'fulfilled') {
try {
// 声明result变量来接收函数执行结果
let result = onResolved(this.PromiseResult)
// 对result的结果进行判断
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
// 失败时的then方法的调用
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult)
}
// 状态为pending时then方法的调用
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: onResolved,
onRejected: onRejected
})
}
})
此时我们再来打印res的值,如图
这样一来,我们就成功实现了与js内置promise函数相同的执行结果。
我们再来看下面这种情况
来看打印结果:
这时候我们遇到了跟上面一样的问题,那同样的,我们再次在js文件里对then方法里,对判断为‘rejected’的语句进行相同的操作
状态为失败的代码
// 失败时的then方法的调用
if (this.PromiseState === 'rejected') {
try {
// 声明result变量来接收函数执行结果
let result = onRejected(this.PromiseResult)
// 对result的结果进行判断
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
只不过这时候执行的函数为onRejected,来看看打印结果:
此时我们就完成了打印,取得了相应的成功结果。
状态为"pending"时的代码
同样的,我们可以把判断状态为'pending'时的代码也完善一下,这时候的函数我们可以进行替换,在我们新定义的函数内部利用try...catch来完善代码逻辑,具体代码如下:
if (this.PromiseState === 'pending') {
this.callbacks.push(
{
onResolved: function () {
try {
//执行成功回调函数
let result = onResolved(self.PromiseResult);
//判断
if (result instanceof Promise) {
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
},
onRejected: function () {
try {
//执行成功回调函数
let result = onRejected(self.PromiseResult);
//判断
if (result instanceof Promise) {
result.then(v => {
resolve(v);
}, r => {
reject(r);
})
} else {
resolve(result);
}
} catch (e) {
reject(e);
}
}
}
)
}
这里回顾复习一下,如果判断当前实例的状态为"pending"时,我们把onResolved函数和 onRejected函数整体打包成对象追加到callbacks数组里,当定时器被触发时,promise构造函数内部便会执行相应函数。
判断函数传参是否完整
此时我们再来进行如下测试,当script模块里只有一个箭头函数时,那这时候js文件中then方法只接受了一个函数作为参数,那么这时候打印结果是什么呢?请看如下代码:
// 欢迎来到 我的世界-
let p = new Promise((resolve, reject) => {
reject('亲')
})
let res = p.then(value => {
console.log(value);
})
console.log(res);
打印结果:
这时候可以看到PromiseResult的值报错,出现了这种情况是因为我们exector函数中是reject(...),在调用then时它应该执行第二个函数,但我们没有设置第二个函数,所以这时候有问题。
那相应的我们该怎么完善代码呢?思路很简单,当调用then函数时,先利用if语句判断传进来的参数是否完整,如果不完整再分类讨论,请看代码
if (typeof onResolved !== 'function') {
onResolved = value => value
}
// 若用户没有传入onRejected箭头函数
if (typeof onRejected !== 'function') {
onRejected = resaon => {
throw resaon
}
}
-
如果判断传进来的onResolved不是一个函数,那么我们直接让它返回其本身的值,并赋值给变量onResolved,参与接下来的运算。
-
如果判断传进来的onRejected不是一个函数,那么我们直接定义一个函数,函数体内抛出值为它本身值的错误,在赋值给onRejected,参与接下来的运算
此时我们再来打印结果,如图
这时候我们就完成了res值的正确打印,这里大家可以先停一停,代码书写完成之后,自己在脑海里走一下刚才的逻辑和运算
封装函数
此时我们发现上面三种状态里的代码大致相同,那我们此时可以对这些函数进行封装,命名为common,所以当我们需要的时候,执行对应的状态里的common函数就行了,来看代码
代码实现
// 返回一个promise对象
return new Promise((resolve, reject) => {
// 封装common函数
function common(type) {
try {
//type为传进来的参数,实质为一个函数
// 执行函数,并且把执行结果赋值给result变量
let result = type(self.PromiseResult)
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
// 成功时的then
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
//调用common函数
common(onResolved)
})
}
// 失败时的then
if (this.PromiseState === 'rejected') {
setTimeout(() => {
//调用common函数
common(onRejected);
})
}
// 异步执行的then
if (this.PromiseState === 'pending') {
self.callbacks.push(
{
onResolved: function (data) {
common(onResolved)
},
onRejected: function (data) {
common(onRejected)
}
}
)
}
})
这样一来我们就完成了对common函数的封装,大大的提高了代码的可读性和维护性,并改善了代码的可移植性。
对promise函数挂载方法
如果我们此时直接调用promise的resolve方法,传一个参数进去,让p来接受整体的返回值,打印p又会有什么结果呢
打印结果:
可以看到控制报错说Promise.resolve不是一个函数,这是因为我们没有在promise构造函数上挂载相应函数,因此我们要在promise身上分别挂载resolve方法和reject方法
注意:这里注意区分函数体内部添加新函数和函数上挂载新方法不一样!
resolve函数
// 给构造函数Promise挂载resolve函数
Promise.resolve = function (data) {
return new Promise((resolve, reject) => {
if (data instanceof Promise) {
data.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(data)
}
})
}
简单理解一下,挂载的resolve函数执行结果是一个新的promise对象,再来对传进来的data数据的类型,如果promise类型,那么再从在表达式里面进行判断,直到最终的结果为非promise类型。如果传进来的是一个非promise类型,则直接执行构promise类型里的resolve方法,对实例进行绑定结果值和状态值
reject函数
// 给构造函数Promise挂载reject函数
Promise.reject = function (data) {
return new Promise((resolve, reject) => {
reject(data)
})
}
这里与上面的唯一不同点就是我们无需对reject里data值的类型进行判断,直接调用promise构造函数里的reject方法就可对实例进行绑定结果值和状态值
此时我们再来打印,如图
结果显示成功打印,说明我们对promise挂载的两个方法及编写无误
注意: 这里的resolve和reject方法均返回的是一个promise对象
本节总结
这节课我们实现了如下的预期目标
- 完善then方法的内部代码
- 封装common函数,提高代码的可读性
- 在promise构造函数上挂载一个resolve和reject方法
注意编写代码之前一定要先搞清出我们要实现的功能是什么,可能需要用到哪些方法,逻辑怎么样能够衔接起来,最后再来整理思路,编写代码。
在后面的文章当中,我们要对promise构造函数继续挂载catch方法,all方法和race方法。这节课先到这里,谢谢大家
html代码
// 欢迎来到 我的世界
// let p = Promise.resolve('132')
// 基本格式
let p = new Promise((resolve, reject) => {
resolve()
// reject()
})
let res = p.then(value => {
// ...
}, reason => {
// ...
})
console.log(p);
可参考本节内的代码复制模块
js文件代码
// 定义promise构造函数
function Promise(executor) {
// 设置默认状态和默认值
this.PromiseState = 'pending'
this.PromiseResult = null
// 设置一个空数组,异步时调用
this.callbacks = []
// this赋给self
const self = this
// 成功时的函数
function resolve(data) {
// 判断此时的p的状态是否已经改变
if (self.PromiseState !== 'pending') return
// 设置实例成功时的状态和结果值
self.PromiseState = 'fulfilled'
self.PromiseResult = data
// 执行调用函数为resolve的异步任务
self.callbacks.forEach(item => {
item.onResolved(data)
})
}
// 失败时的函数
function reject(data) {
// 判断此时的p的状态是否已经改变
if (self.PromiseState !== 'pending') return
// 设置实例成功时的状态和结果值
self.PromiseState = 'rejected'
self.PromiseResult = data
// 执行调用函数为reject的异步任务
self.callbacks.forEach(item => {
item.onRejected(data)
})
}
// 执行函数并捕捉可能存在的异常
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
// 在promise函数原型上挂载一个then方法
Promise.prototype.then = function (onResolved, onRejected) {
const self = this
// 若用户没有传入onResolved箭头函数
if (typeof onResolved !== 'function') {
onResolved = value => value
}
// 若用户没有传入onRejected箭头函数
if (typeof onRejected !== 'function') {
onRejected = resaon => {
throw resaon
}
}
return new Promise((resolve, reject) => {
// 封装common函数
function common(type) {
try {
// 执行函数,并且把执行结果赋值给result变量
let result = type(self.PromiseResult)
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
// 成功时的then
if (this.PromiseState === 'fulfilled') {
// setTimeout(() => {
common(onResolved)
// })
}
// 失败时的then
if (this.PromiseState === 'rejected') {
// setTimeout(() => {
common(onRejected);
// })
}
// 异步执行的then
if (this.PromiseState === 'pending') {
self.callbacks.push(
{
onResolved: function (data) {
common(onResolved)
},
onRejected: function (data) {
common(onRejected)
}
}
)
}
})
}
// 给构造函数Promise挂载resolve函数
Promise.resolve = function (data) {
return new Promise((resolve, reject) => {
if (data instanceof Promise) {
data.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(data)
}
})
}
// 给构造函数Promise挂载reject函数
Promise.reject = function (data) {
return new Promise((resolve, reject) => {
reject(data)
})
}