hello!大家好,我是一名正在学习前端技术的大学生,欢迎大家关注我,一起探讨前端技术。
前言
promise是JavaScript里的一个强大的异步操作工具,经常出现在面试题中,并且异步请求函数axios也是基于promise实现的,由此可见promise的强大,在此之前我通过B站等网站观看过多个教程,也在掘金等博客上看过多篇文章,以下是我学习之后对promise的总结和理解。本文我们手写一个promise,深入理解promise。
正文
根据promise对象,先搭建一个基本框架
function MyPromise(excutor) {
//resovle函数
function resolve(value){}
//reject函数
function reject(reason){}
//调用执行器函数
excutor(resolve, reject)
}
//添加.then方法
MyPromise.prototype.then = function (OnResolved, OnRejected) {}
在promise中有三个状态(pending,fulfilled,rejected)和结果值,所以应该在对象中声明变量存储状态和结果值,并且在调用resolve函数和reject函数时修改状态和结果。因为在调用resolve函数和reject函数时this的指向为window,所以应该在函数外面声明变量存储promise对象的this值。
this.PromiseState = 'pending'
this.PromiseResult = null
const self = this
function resolve(value) {
//修改对象状态
self.PromiseState = 'fulfilled'
//设置对象结果值
self.PromiseResult = value
}
//reject函数
function reject(reason) {
//修改对象状态
self.PromiseState = 'rejected'
//设置对象结果值
self.PromiseResult = reason
}
因为在promise实例中抛出异常后接收的应该是失败的promise实例对象,在MyPromise中抛出异常却收到控制台的报错,如图
所以应该想到用try{}.catch{}来对抛出异常做出处理,由于执行抛出异常的函数是执行器函数,应该在调用执行器函数之前加try{}.catch{}判断是否为抛出异常。
const p = new MyPromise((resolve, reject) => {
throw "ERROR"
})
try {
excutor(resolve, reject)
} catch (e) {
reject(e)
}
在promise对象中因为修改状态和结果值只能修改一次,所以应该在修改状态和结果值之前判断状态值是否有被更改(即PromiseState是否为pending)。以下用resolve函数举例(reject函数同理):
this.PromiseState = 'pending'
this.PromiseResult = null
function resolve(value) {
//判断Promise对象状态是否被改变
if (self.PromiseState !== 'pending') return
//修改对象状态
self.PromiseState = 'fulfilled'
//设置对象结果值
self.PromiseResult = value
}
在.then方法里成功才执行OnResolved;失败才执行OnRejected,所以要先判断是否成功。
if (this.PromiseState === 'fulfilled') {
OnResolved(this.PromiseResult) //执行函数需要传递参数
}
if (this.PromiseState === 'rejected') {
OnRejected(this.PromiseResult)
}
这里能直接使用this的原因是调用.then方法的是promise实例对象,所以它的this指向为promise对象。
继续补充then方法,then方法返回的应该是一个promise对象,并且对抛出异常做出相应的处理(使用try{}catch{}包裹),由于只是单纯的调用OnResolved函数或OnRejected函数并没有改变Promise对象的状态(即状态仍为pending),所以应该声明变量接收OnResolved函数或OnRejected函数返回的值并做出判断(补充:使用instanceof判断,举例:A instanceof B,可以判断A属性是否在B的原型链上),如果为一个promise对象,则调用它本身的.then方法,如果不为promise对象,则直接调用resolve或reject函数修改promise的状态并返回结果值。
return new MyPromise((resolve, reject) => {
if (this.PromiseState === 'fulfilled') {
//对抛出异常做出回应
try {
//return后状态没有被改变
//获取回调函数的执行结果
let result = OnResolved(this.PromiseResult)
//对执行结果进行判断
if (result instanceof Promise) {
//如果为Promise
result.then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
if (this.PromiseState === 'rejected') {
OnRejected(this.PromiseResult)
}
})
}
if (this.PromiseState === 'rejected') {
//对抛出异常做出回应
try {
//return后状态没有被改变
//获取回调函数的执行结果
let result = OnRejected(this.PromiseResult)
//对执行结果进行判断
if (result instanceof Promise) {
//如果为Promise
result.then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
这里应该有人会问状态为rejected,返回的结果不是promise对象为什么要把状态改为fulfilled,因为它返回结果为undefined,所以调用.then方法后状态会修改为fulfilled。
异步任务
在异步任务中因为执行代码是从上往下,所以执行到then方法时由于没有拿到状态值而返回的promise对象状态为pending,所以要在then方法的pending做出判断,由于调用回调函数的为resolve函数和reject函数,应该将要执行的回调保存起来以便调用,由于可能会多次调用.then方法,所以应该声明数组来保存回调。
//在MyPromise中声明数组保存回调
this.callbacks = []
//在resolve函数和reject函数调用相应的回调
//resolve函数
self.callbacks.forEach(item => item.OnResolved(value))
//reject函数
self.callbacks.forEach(item => item.OnRejected(reason))
//then方法中加pending的判断
if (this.PromiseState === 'pending') {
//因为调用回调函数的应该是resolve函数,所以在这里应该保存回调
this.callbacks.push(
{
OnResolved,
OnRejected
}
)
}
异步任务中因为在pending状态下调用.then方法并没有调用resolve函数或reject函数导致promise对象状态并未改变(处于pending状态),所以应该在OnResolved和OnRejected函数中声明函数以便调用时可以改变promise状态,在调用函数前应该对抛出异常做出处理。
OnResovled: function () {
//对抛出异常做出处理
try {
//因为这里的this指向并不是promise对象,应该声明变量存储
//接收回调返回的值判断是否为一个promise对象
let result = OnResolved(self.PromiseResult)
//判断
if (result instanceof Promise) {
result.then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
},
OnRejectd: function () {
//对抛出异常做出处理
try {
let result = OnRejectd(self.PromiseResult)
//判断
if (result instanceof Promise) {
result.then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
添加catch方法
实现异常穿透和值传递
const p = new MyPromise((resolve, reject) => {
reject('error')
})
p.then(value => {
console.log(111)
}).then(value => {
console.log(222)
}).then(value => {
console.log(333)
}).catch(
console.warn('error')
)
因为在.then方法中只有一个参数并没有状态为rejected的回调函数的参数,所以在进入.then方法后就会报错误OnRejectd is not a function,如图:
所以在.then方法中应该进行回调函数的参数判断,如果它不是一个函数,就追加一个函数做为参数。
//判断回调函数参数
if (typeof OnRejected !== 'function') {
OnRejected = reason => {
throw reason
}
}
值传递
//值传递
const res = p.then(
).then(value => {
console.log(222)
}).then(value => {
console.log(333)
}).catch(
console.warn('error')
)
console.log(res)
原理相同,由于.then中没有状态值为resolved的回调函数参数,所以需要进行回调函数参数判断,如果不是一个function,则追加函数作为参数。
if (typeof OnResolved !== 'function') {
OnResolved = value => value
}
添加.resolve方法和.reject方法
因为.resolve方法和.reject方法是属于promise函数对象的,所以不用在prototype里面追加函数。
//添加promise.resolve方法,.resolve是属于promise函数对象的,
MyPromise.resolve = function (value) {
return new MyPromise((resolve, reject) => {
//判断value是否为promise对象
if (value instanceof Promise) {
value.then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
} else {
resolve(value)
}
})
}
//添加promise.reject方法
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
添加.all方法和.race方法
.all方法:接收一个promise数组,当全部都为成功时返回一个成功值所组成的数组,如图:
如果其中有一个为失败是,则返回该失败的promise对象,如图:
.race方法:接收一个promise数组,谁先被调用返回谁的结果值 测试函数如图:
测试结果如图:
//添加pomise.all方法(当全部都为成功是返回成功值所组成的数组,其中一个失败则返回一个失败的promise)
MyPromise.all = function (promises) {
//声明数组存储返回的PromiseResult值
let arr = []
return new MyPromise((resolve, reject) => {
//遍历
for (let i = 0; i < promises.length; i++) {
promises[i].then(rev => {
arr[i] = rev
if (i == promises.length - 1) resolve(arr)
}, rej => {
reject(rej)
})
}
})
}
//添加promise.race方法(谁先被调用返回谁的结果)
MyPromise.race = function (promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
}
})
}
完整代码如下:
function MyPromise(excutor) {
//声明变量存储状态和结果
this.PromiseState = 'pending'
this.PromiseResult = null
//声明变量保存回调函数
//因为多次调用then方法时,只用对象无法全部保存执行的回调
this.callbacks = []
//保存实例对象的this值
//在函数resolve里的this指向为window而不是实例对象
let self = this
//resovle函数
function resolve(value) {
//因为修改状态只能修改一次,所以应该提前判断保存的状态值是否有被修改过即(状态值是否为pending)
if (self.PromiseState !== 'pending') return
//修改对象状态
self.PromiseState = 'fulfilled'
//设置对象结果值
self.PromiseResult = value
//调用成功的回调函数
self.callbacks.forEach(item => item.OnResolved(value))
}
//reject函数
function reject(reason) {
if (self.PromiseState !== 'pending') return
//修改对象状态
self.PromiseState = 'rejected'
//设置对象结果值
self.PromiseResult = reason
//调用成功的回调函数
self.callbacks.forEach(item => item.OnRejected(reason))
}
//调用执行器函数
//因为执行抛出错误异常的函数是执行器函数,所以应该在调用执行器函数的时候加上try catch
try {
excutor(resolve, reject)
} catch (e) {
reject(e)
}
}
//添加then方法
MyPromise.prototype.then = function (OnResolved, OnRejected) {
//判断回调函数参数
if (typeof OnRejected !== 'function') {
OnRejected = reason => {
throw reason
}
}
if (typeof OnResolved !== 'function') {
OnResolved = value => value
}
//then方法应该返回的是一个promise对象
return new MyPromise((resolve, reject) => {
//声明变量存储this值
let self = this
//执行回调
//因为成功才执行OnResolved,失败才执行Onrejected,所以要提前进行判断
//因为调用then方法的是promise对象,所以this指向也为promise
if (this.PromiseState === 'fulfilled') {
//对抛出异常做出回应
try {
//return后状态没有被改变
//获取回调函数的执行结果
let result = OnResolved(this.PromiseResult) //因为执行函数需要有传递参数
//对执行结果进行判断
if (result instanceof Promise) {
//如果为Promise
result.then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
if (this.PromiseState === 'rejected') {
//对抛出异常做出回应
try {
//return后状态没有被改变
//获取回调函数的执行结果
let result = OnRejected(this.PromiseResult) //因为执行函数需要有传递参数
//对执行结果进行判断
if (result instanceof Promise) {
//如果为Promise
result.then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
//因为在异步执行中,因为resolve函数没有调用,状态没有改变,所以then方法中不能正常输出
//所以应该加个pending的判断条件
if (this.PromiseState === 'pending') {
//因为调用回调函数的应该是resolve函数,所以在这里应该保存回调
this.callbacks.push(
{
//异步任务中,pending状态下没有调用resolve函数或reject函数
//所以并未改变promise状态,当改变状态后会执行该函数
OnResolved: function () {
//对抛出异常做出处理
try {
//因为这里的this指向并不是promise对象,应该声明变量存储
//接收回调返回的值判断是否为一个promise对象
let result = OnResolved(self.PromiseResult)
//判断
if (result instanceof Promise) {
result.then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
},
OnRejected: function () {
//对抛出异常做出处理
try {
let result = OnRejected(self.PromiseResult)
//判断
if (result instanceof Promise) {
result.then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
}
)
}
})
}
//添加catch方法
MyPromise.prototype.catch = function (OnRejected) {
//因为catch方法只是对错误做出响应所以可以直接使用.then方法,并且第一个参数为undefined
return this.then(undefined, OnRejected)
}
//添加promise.resolve方法,.resovle是属于promise函数对象的,
MyPromise.resolve = function (value) {
return new MyPromise((resolve, reject) => {
//判断value是否为promise对象
if (value instanceof Promise) {
value.then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
} else {
resolve(value)
}
})
}
//添加promise.reject方法
MyPromise.reject = function (reason) {
return new MyPromise((resolve, reject) => {
reject(reason)
})
}
//添加pomise.all方法(当全部都为成功是返回成功值所组成的数组,其中一个失败则返回一个失败的promise)
MyPromise.all = function (promises) {
//声明数组存储返回的PromiseResult值
let arr = []
return new MyPromise((resolve, reject) => {
//遍历
for (let i = 0; i < promises.length; i++) {
promises[i].then(rev => {
arr[i] = rev
if (i == promises.length - 1) resolve(arr)
}, rej => {
reject(rej)
})
}
})
}
//添加promise.race方法(谁先被调用返回谁的结果)
MyPromise.race = function (promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(rev => {
resolve(rev)
}, rej => {
reject(rej)
})
}
})
}
结语
小伙伴们,以上是我学习后对promise的理解,还请多在评论区留言,大家相互讨论相互学习。