本文参考了此博客,在网上浏览了很多关于Promise的相关资料,promise可以说是JavaScript的重难点了,其中,很是感谢阮一峰老师的博客讲解以及MDN上的文档教程
如对Promise不太熟悉的同学,可以先浏览Promise工作原理分析,如有不同意见,欢迎讨论,一起进步。
function doSomething(){
var p1 = new Promise(function (resolve, reject) {
setTimeout(()=>{ //setTimeout1
console.log('Start do something')
resolve('Success!')
},1000)
})
return p1
}
function successCallback(data){
var p2= new Promise(function (resolve, reject) {
setTimeout(()=>{ //setTimeout2
console.log(data)
resolve()
},1000)
})
return p2
}
function failCallback(data){
var p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
console.log('Error1!')
resolve() //setTimeout3
},1000)
})
return p3
}
//接下来去调用一下
doSomething() //promise1
.then(
(data)=>{ //promise2
return successCallback(data)},
(data)=>{
return failCallback(data)}
)
.then(()=>{ //promise3
return new Promise((resolve,rejected)=>{
rejected('Unknown Error')
})
})
.catch(res=>{ //promise4
console.log(res)
})
输出:
Start do something
Success!
Unknown Error
过程:
构造函数Promise必须接受一个函数对象作为参数,我们称其为handle,handle又包含了resolve和reject两个参数,这两个参数都是函数对象。
定义一个判断变量是否为函数的方法,后面会用到:
// 判断变量否为function
const isFunction = variable => typeof variable === 'function'
首先我们定义一个MyPromise的class,它接收一个函数handle作为参数
class MyPromise {
constructor (handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
}
}
我们从上方的过程中可以知道,promise主体执行异步程序,然后根据是否完成异步程序设置promise的状态,然后根据promise的状态调用then()方法。
所以,我们也为MyPromise添加一个状态标识
状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态。
resolve和reject做了什么?
// 定义Promise的三种状态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
为MyPromise添加状态和值,并添加状态改变的执行逻辑:
注意: MyPromise的状态只能改变一次,即状态改变之前MyPromise的状态只能是PENDING。
class MyPromise {
constructor (handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
// 添加状态
this._status = PENDING
// 添加状态
this._value = undefined
// 执行handle
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// 添加resovle时执行的函数
_resolve (val) {
if (this._status !== PENDING) return
this._status = FULFILLED
this._value = val
}
// 添加reject时执行的函数
_reject (err) {
if (this._status !== PENDING) return
this._status = REJECTED
this._value = err
}
}
这里我们需要理解:
handle(this._resolve.bind(this),this._reject.bind(this))
我之前阅读这一句代码时陷入了思想误区,这里,我将详细展开为什么要这样写。
观察如下代码:
class myTest{
constructor(handle) {
this._sum = undefined
handle(this._add.bind(this))
}
_add(a,b){
this._sum = a + b
return this._sum
}
}
let test = new myTest((doSomething)=>{
let [a, b] = [1, 2]
console.log(doSomething(a,b))
})
在创建myTest时,传入了一个箭头函数:
(doSomething)=>{let [a, b] = [1, 2];console.log(doSomething(a,b))}
这个箭头函数就是myTest中的handle,然后调用了handle(this._add.bind(this)),这里向handle传入的参数为this._add函数对象,也就是说,这个箭头函数中的doSomething被赋值为了this._add。
平时我们常见到的参数赋值都是实例变量的赋值,而这里是函数变量的赋值,所以有一点难以理解,但是其实和实例变量赋值是一样的思想。
这里还涉及了this指向的知识点,bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。如有疑惑可以查看我的这一篇博客
我们已经知道promise的then()方法会接收两个函数对象作为参数,分别对应着promise状态为onFulfilled和onRejected的回调函数
promise.then(onFulfilled, onRejected)
promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2);
更多的promise值的传递和错误捕获机制可以查看这里
在MyPromise中添加then()方法:
// 添加then方法
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const { _value, _status } = this //参数结构
switch (_status) {
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
onFulfilled(_value)
break
case REJECTED:
onRejected(_value)
break
}
}
思维梳理: 首先then()函数接收了两个函数对象作为参数并根据当前MyPromise的状态选择执行,这里我们通过了一个switch 实现。然后,实现 onFulfilled和onRejected的参数传递,我们在前面的学习中知道,promise.then(value=>{},reason=>{})向回调函数传入的参数就是promise对象的值,对应到MyPromise也是MyPromise._value,const { _value, _status } = this
就从MyPromise对象中获取到了MyPromise的值和状态。
难点:请注意观察上面的then方法,大家觉得有没有问题呢?看下面的代码:
let p1 = new MyPromise((resolve,reject)=>{
resolve('OK')
})
let p2 = new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('OK')
},1000)
})
p1.then((value)=>{
console.log(value)
},(reason)=>{
console.warn(reason)
})
p2.then((value)=>{
console.log(value)
},(reason)=>{
console.warn(reason)
})
p1和p2唯一的区别是p1直接执行了resolve()方法,而p2执行了一个异步任务,在一段时间后才执行了resolve()方法。
先看p1,在执行p1.then()的时候,p1已经执行了resolve()方法,也就是说p1的状态已经由pending->fulfilled,所以执行then()方法时可以根据状态选择执行哪一个回调函数。
再看p2,在执行then()方法时,p2的状态仍然是pending,这个then方法不会做任何事情,由于状态未改变,switch中找不到对应的回调函数,所以就不会执行回调函数。当setTimeout执行完之后,p2的状态有pending->fulfilled,但此时的then()已经执行完毕,所以也不会执行回调函数。
为了解决这个问题,我们该如何做呢?
在switch中添加一个pending状态?当执行then()时,若MyPromise的状态为pending,我们又该做什么?
为了解决异步任务完成之后我们仍能调用回调函数,我们可以先将then()中的两个函数对象保存起来,所以这需要我们在MyPromise中增加两个任务队列,分别保存成功和失败的回调函数。
// 添加成功回调函数队列
this._fulfilledQueues = []
// 添加失败回调函数队列
this._rejectedQueues = []
当状态为pending时,将then方法回调函数加入执行队列等待执行
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(onFulfilled)
this._rejectedQueues.push(onRejected)
break
为什么用 [ ] 来保存回调而不是用一个 {字典} 来保存呢?观察下面的代码
//保存回调
this._callback={}
case PENDING:
this._callback = {
onFulfilled:onFulfilled,
onRejected:onRejected
}
let p2 = new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('OK')
},1000)
})
p2.then((value)=>{
console.log(value)
},(reason)=>{
console.warn(reason)
})
p2.then((value)=>{
console.log(value+'then second callback')
},(reason)=>{
console.warn(reason)
})
这里有两个p2的promise链,也就是说当p2的状态改变后,着两条链都会继续进行下去,如果使用字典,那么第一个then()的回调就被第二个then()覆盖掉了
(这里保存回调有多种实现,看大家的喜好,只要全部保存下来并且不会覆盖就可以)
现在问题又来了,我们保存了回调函数,又该什么时候执行呢?
当MyPromise状态改变的时候!=>也就是调用_resolve()或_reject()的时候,所以我们需要更新MyPromise的_resolve()和_reject()。
_resolve (val) {
if (this._status !== PENDING) return
this._status = FULFILLED
this._value = val
let callback
while (callback = this._fulfilledQueues.shift()){
callback(this._value)
}
}
_reject (err) {
if (this._status !== PENDING) return
this._status = REJECTED
this._value = err
let callback
while (callback = this._rejectedQueues.shift()){
callback(this._value)
}
}
继续完善then()方法。
为了保证MyPromise可以链式调用,所以then()方法需要返回一个MyPromise。
我们先看看promise的then()如何返回一个新的promise:
let p = new Promise((resolve,reject)=>{
resolve('OK')
})
let p1 = p.then(value => {
return value
})
let p2 = p.then(value => {
return new Promise((resolve, reject)=>{
resolve('Return a promise')
})
})
let p3 = p.then(value => {
return new Promise((resolve, reject)=>{
reject('Error')
})
})
let p4 = p.then(value => {
return new Promise((resolve, reject)=>{
})
})
let p5 = p.then(value => {
throw Error('Error!')
})
console.log(p1)
console.log(p2)
console.log(p3)
console.log(p4)
console.log(p5)
根据以上三点,我们来改写MyPromise的then()方法:
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const self = this
return new MyPromise((resolve,reject)=>{
switch (this._status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(onFulfilled)
this._rejectedQueues.push(onRejected)
break
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
try {
let result = onFulfilled(this._value) //获取到回调函数的结果
//根据result来设置返回的MyPromise
if(result instanceof MyPromise){
//如果回调函数返回了一个MyPromise对象
result.then( v => { //v是result执行成功后的_value值
resolve(v) //根据result的状态值来设置返回的MyPromise的状态值和对象值
},r => { //r是result执行失败后的_valur值
reject(r)
})
}else {
//如果回调函数返回的是一个简单值
resolve(result)
}
}catch (e) {
//如果回调函数抛出了异常
reject(e)
}
break
case REJECTED:
try {
let result = onRejected(this._value) //获取到回调函数的结果
//根据result来设置返回的MyPromise
if(result instanceof MyPromise){
//如果回调函数返回了一个MyPromise对象
result.then( v => { //v是result执行成功后的_value值
resolve(v) //根据result的状态值来设置返回的MyPromise的状态值和对象值
},r => { //r是result执行失败后的_valur值
reject(r)
})
}else {
//如果回调函数返回的是一个简单值
resolve(result)
}
}catch (e) {
//如果回调函数抛出了异常
reject(e)
}
break
}
})
}
在仔细阅读上面的代买之后,不知道大家有没有发现一个问题,我们只修改了同步任务下的then()方法,如果此时MyPromise需要执行一个异步任务:
let myPromise = new MyPromise((resolve,reject)=>{
setTimeout(()=>{
resolve('OK')
},1000)
})
myPromise.then((value)=>{
return new MyPromise((resolve,reject)=>{
resolve(value + ' with returning a new MyPromise')
})
}).then(value=>{
console.log(value)
})
then()方法发现myPromise的状态为PENDING,所以会将回调函数放入数组,而在PENDING分支中并没有对返回的MyPromise对象进行设置。当myPromise的异步任务完成,其状态改变之后,会在_resolve()或_reject()中调用之前存入数组中的回调函数。改变_resolve()和_reject()方法,在_resolve()和_reject()中设置then()方法返回的MyPromise?这样明显很难实现,因为then()方法的返回值和_resolve()、_reject()的作用域都不一样。
所以,不妨换一个思路,之前我们是向数组中直接添加了回调函数:
case PENDING:
this._fulfilledQueues.push(onFulfilled)
this._rejectedQueues.push(onRejected)
现在,我们可以向数组中添加一个包含了回调函数和改变返回的MyPromise对象的混合函数。
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const self = this
return new MyPromise((resolve,reject)=>{
switch (this._status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(()=>{
try {
let result = onFulfilled(self._value)
//这里和FULFILLED和REJECTED分支对返回对象的处理相似
//根据result来设置返回的MyPromise
if(result instanceof MyPromise){
//如果回调函数返回了一个MyPromise对象
result.then( v => { //v是result执行成功后的_value值
resolve(v) //根据result的状态值来设置返回的MyPromise的状态值和对象值
},r => { //r是result执行失败后的_valur值
reject(r)
})
}else {
//如果回调函数返回的是一个简单值
resolve(result)
}
}catch (e) {
//如果回调函数抛出了异常
reject(e)
}
})
this._rejectedQueues.push(()=>{
try {
let result = onRejected(self._value)
//这里和FULFILLED和REJECTED分支对返回对象的处理相似
//根据result来设置返回的MyPromise
if(result instanceof MyPromise){
//如果回调函数返回了一个MyPromise对象
result.then( v => { //v是result执行成功后的_value值
resolve(v) //根据result的状态值来设置返回的MyPromise的状态值和对象值
},r => { //r是result执行失败后的_valur值
reject(r)
})
}else {
//如果回调函数返回的是一个简单值
resolve(result)
}
}catch (e) {
//如果回调函数抛出了异常
reject(e)
}
})
break
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
try {
let result = onFulfilled(self._value) //获取到回调函数的结果
//根据result来设置返回的MyPromise
if(result instanceof MyPromise){
//如果回调函数返回了一个MyPromise对象
result.then( v => { //v是result执行成功后的_value值
resolve(v) //根据result的状态值来设置返回的MyPromise的状态值和对象值
},r => { //r是result执行失败后的_valur值
reject(r)
})
}else {
//如果回调函数返回的是一个简单值
resolve(result)
}
}catch (e) {
//如果回调函数抛出了异常
reject(e)
}
break
case REJECTED:
try {
let result = onRejected(self._value) //获取到回调函数的结果
//根据result来设置返回的MyPromise
if(result instanceof MyPromise){
//如果回调函数返回了一个MyPromise对象
result.then( v => { //v是result执行成功后的_value值
resolve(v) //根据result的状态值来设置返回的MyPromise的状态值和对象值
},r => { //r是result执行失败后的_valur值
reject(r)
})
}else {
//如果回调函数返回的是一个简单值
resolve(result)
}
}catch (e) {
//如果回调函数抛出了异常
reject(e)
}
break
}
})
}
这一部分难以理解,我们需要和前面的then()分析联系起来反复斟酌。
如果MyPromise的状态为rejected,而then()只传入了fulfilled状态对用的回调函数,这有出现了一个新的问题,onRejected is not defined……
所以,我们再完善then(),当只传入一个回调函数的情况下:
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const self = this
if (!isFunction(onRejected)){
onRejected = reason =>{
throw reason
}
}
return new MyPromise(……)
}
其实,这就是promise的异常渗透机制的实现
现在,我们的then()就算是达到了期望,但是,我们不难发现,在then()方法中,出现了四次try……catch……,而且功能类似,为了方便后期维护,防止代码冗余,我们可以将这段代码封装起来。
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const self = this
return new MyPromise((resolve,reject)=>{
function callback(action){
try {
let result = action(self._value) //获取到回调函数的结果
//根据result来设置返回的MyPromise
if(result instanceof MyPromise){
//如果回调函数返回了一个MyPromise对象
result.then( v => { //v是result执行成功后的_value值
resolve(v) //根据result的状态值来设置返回的MyPromise的状态值和对象值
},r => { //r是result执行失败后的_valur值
reject(r)
})
}else {
//如果回调函数返回的是一个简单值
resolve(result)
}
}catch (e) {
//如果回调函数抛出了异常
reject(e)
}
}
switch (this._status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(()=>{
callback(onFulfilled)
})
this._rejectedQueues.push(()=>{
callback(onRejected)
})
break
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
callback(onFulfilled)
break
case REJECTED:
callback(onRejected)
break
}
})
}
Promise.prototype.catch()方法是用于指定发生错误时的回调函数
所以,我们在实现catch时,可以借助then():
MyPromise.prototype.catch = function (onRejected){
return this.then(undefined,onRejected)
}
// 添加静态resolve方法
static resolve (value) {
// 如果参数是MyPromise实例,直接返回这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
// 添加静态reject方法
static reject (value) {
return new MyPromise((resolve ,reject) => reject(value))
}
// 添加静态all方法
static all (list) {
return new MyPromise((resolve, reject) => {
/**
* 返回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err)
})
}
})
}
// 添加静态race方法
static race (list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
finally (callback) {
return this.then(
value => MyPromise.resolve(callback()).then(() => value),
reason => MyPromise.resolve(callback()).then(() => { throw reason })
);
};
最后,附上全部代码:
// 判断变量否为function
const isFunction = variable => typeof variable === 'function'
// 定义Promise的三种状态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
// 添加状态
this._status = PENDING
// 添加状态
this._value = undefined
// 添加成功回调函数队列
this._fulfilledQueues = []
// 添加失败回调函数队列
this._rejectedQueues = []
// //保存回调
// this._callback={}
// 执行handle
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// 添加resovle时执行的函数
_resolve (val) {
if (this._status !== PENDING) return
this._status = FULFILLED
this._value = val
let callback
while (callback = this._fulfilledQueues.shift()){
callback(this._value)
}
}
// 添加reject时执行的函数
_reject (err) {
if (this._status !== PENDING) return
this._status = REJECTED
this._value = err
let callback
while (callback = this._rejectedQueues.shift()){
callback(this._value)
}
}
// 添加静态resolve方法
static resolve (value) {
// 如果参数是MyPromise实例,直接返回这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
// 添加静态reject方法
static reject (value) {
return new MyPromise((resolve ,reject) => reject(value))
}
// 添加静态all方法
static all (list) {
return new MyPromise((resolve, reject) => {
/**
* 返回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err)
})
}
})
}
// 添加静态race方法
static race (list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
finally (callback) {
return this.then(
value => MyPromise.resolve(callback()).then(() => value),
reason => MyPromise.resolve(callback()).then(() => { throw reason })
);
};
}
// 添加then方法
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const self = this
if (!isFunction(onRejected)){
onRejected = reason =>{
throw reason
}
}
return new MyPromise((resolve,reject)=>{
function callback(action){
try {
let result = action(self._value) //获取到回调函数的结果
//根据result来设置返回的MyPromise
if(result instanceof MyPromise){
//如果回调函数返回了一个MyPromise对象
result.then( v => { //v是result执行成功后的_value值
resolve(v) //根据result的状态值来设置返回的MyPromise的状态值和对象值
},r => { //r是result执行失败后的_valur值
reject(r)
})
}else {
//如果回调函数返回的是一个简单值
resolve(result)
}
}catch (e) {
//如果回调函数抛出了异常
reject(e)
}
}
switch (this._status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(()=>{
callback(onFulfilled)
})
this._rejectedQueues.push(()=>{
callback(onRejected)
})
break
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
callback(onFulfilled)
break
case REJECTED:
callback(onRejected)
break
}
})
}
MyPromise.prototype.catch = function (onRejected){
return this.then(undefined,onRejected)
}