深入Promise(一)

216 阅读8分钟

在平时的工作中,我们不难认识到,想优雅地进行异步操作,必须要熟识一个极其重要的概念——Promise,它是取代传统函数回调,实现同步链式写法的一种解决方案。但是,对于很多像我这样的初学者来说,Promise并不是很好理解,所以,本文接下来将带你由浅入深剖析Promise的使用及原理。

异步操作的前世今生

我们先从一个异步网络请求的例子讲起:

function request(url) {
	setTimeout(() => {
		//判断请求是否成功
		if(url === 'http...') {
			//请求成功
			let val = [1,2,3]
                        //return val ?
		} else {
			//请求失败
			let err = "请求失败"
                        //return err ?
		}
	},5000)
}
//页面调用
request("http...")

我们封装了一个request网络请求,想要在页面中传入url调用请求,请求成功则返回一个val的数组,失败则返回"请求失败"的字符串,但是返回成功或失败的值是在异步操作中,无法通过return返回给调用页面,那怎样才能拿到请求的结果呢?
如下,我们可以传入两个回调函数successCallback,failtureCallback,在判断请求成功时调用successCallback,失败时调用failtureCallback,在页面中调用request的时候,除了传入url外,还需传入两个回调函数,在回调函数中接收返回的结果。

function request(url,successCallback,failtureCallback) {
	setTimeout(() => {
		//判断请求是否成功
		if(url === 'http...') {
			//请求成功
			let val = [1,2,3]
			successCallback(val)
		} else {
			//请求失败
			let err = "请求失败"
			failtureCallback(err)
		}
	},5000)
}

////页面调用
request("http...",(res) => {
	console.log(res)
}, (err) => {
	console.log(err)
})

上述操作可以成功拿到异步请求中的返回结果,但是这种方式是有弊端的
别人在使用我们封装的request请求的时候,必须看封装的源码才知道request函数怎么样获取结果
此时,Promise横空出世,对异步回调做出了规范。

function request(url) {
	return new Promise((resolve,reject) => {
		setTimeout(() => {
			//判断请求是否成功
			if(url === 'http...') {
				//请求成功
				let val = [1,2,3]
				resolve(val)
			} else {
				//请求失败
				let err = "请求失败"
				reject(err)
			}
		},5000)
	})	
}

//页面调用
const promist_test = request('http...')
promist_test.then(res => {
	console.log(res)
}).catch(err => {
	console.log(err)
})

Promise 对异步回调做出了规范,该规范规定,Promise接收两个回调函数(resolve和reject)作为参数,请求成功时,调用resolve,请求失败时,调用reject。同时,在页面中使用时,通过then方法接收成功的回调,catch方法接收失败的回调。

Promise 概念

ES6引入Promise,它是一个类,通过new操作符实例化,Promise是一个有状态的对象,创建Promises时,需要传入执行器(executor)函数作为参数,它共有三种状态:

  • [1] pending (待定) : 初始状态,执行executor中的代码时,处于该状态
  • [2] fulfilled/resolved (兑现) :执行resolve时,处于该状态
  • [3] rejected (拒绝) : 执行reject时,处于该状态

Promise的对象方法

对象方法在通过new关键字创建出的对象上使用

then

then是定义在Promise原型对象上的方法,它最多可以接收两个函数作为参数
语法:Promise.then(onFulfilled,onRejected)
onFulfilled 接收fulfilled状态返回的结果 onRejected 接收rejected状态返回的结果

const promise = new Promise((resolve,reject) => {
	resolve(123)
	// reject("errrr")
})

promise.then(res => {
	console.log(res)
},err => {
	console.log(err)
})

上述栗子中,都是只调用了一次then方法,那么,当同一一个Promise被多次调用then方法时,会发生什么呢?

const promise = new Promise((resolve,reject) => {
	resolve(123)
})

promise.then((res) => {
	console.log('res1',res)
})
promise.then((res) => {
	console.log('res2',res)
})
promise.then((res) => {
	console.log('res3',res)
})

打印结果如下:

res1 123
res2 123
res3 123

不难看出,当一个Promise的resolve被调用时,所有then方法传入的回调都会被执行
then的链式调用

const promise = new Promise((resolve,reject) => {
	resolve(2)
})
promise.then(res => {
	return(res*2)
}).then(res => {
	return(res+2)
}).then(res => {
	console.log(res)   //6
})

那么then方法为什么可以这样连续调用呢?下面随我们来揭晓答案:
then方法它本身有返回值,并且它的返回值是Promise,如果then中return一个普通值,那么它会自动给这个值包裹一层Promise。哇偶,原来真相竟是这样!
那么问题来了,如果then中又返回一个Promise呢?

const test = new Promise((resolve,reject) => {
	setTimeout(() => {
		resolve("状态由我决定")
	},5000)
})
const promise = new Promise((resolve,reject) => {
	resolve(2)
})
promise.then(res => {
	return test
}).then(res => {
	console.log(res)     //状态由我决定
})

其实在then方法中return一个Promise时,还是会自动给它包裹一层Promise,但是此时Promise的状态会进行移交,由return的Promise决定。
到这大家应该很容易就能想到还有第三种情况,return一个实现了then方法的对象,同样,它还是自动被包裹一层Promise,但是会进行状态移交,状态由return的这个对象决定,下面简单进行下代码演示:

const obj = {
	then: function(resolve,reject) {
              resolve("状态由我决定")
	     }
}
const promise = new Promise((resolve,reject) => {
	resolve(2)
})
promise.then(res => {
	return obj
}).then(res => {
	console.log(res)     //状态由我决定
})

catch

通过上述对then方法的介绍,我们知道then的第二个参数可以接收reject状态返回的结果,即onRejected 接收rejected状态返回的结果,但是then的这种写法对于代码的阅读性不太友好,所以ES6增加了catch方法来处理reject状态的结果。

const promise = new Promise((resolve,reject) => {
	reject("优先捕获")
        //resolve("OK")
})
promise.then(res => {
	return new Promise((resolve,reject) => {
		reject("也能捕获我")
	})
}).catch(err => {
	console.log(err)     //优先捕获
})

catch不仅能捕获promise中抛出的异常,也能处理then中抛出的异常,但是会优先捕获promise中的异常,当promise为resolve时,才会捕获then中的异常。 catch本身也是有返回值的,它返回一个Promise,catch中return的值也会自动包裹一层Promise,所以它也是可以链式调用的,当然这里return的值和then一样也分三种情况,此处只列举普通值,就不一一赘述了。

const promise = new Promise((resolve,reject) => {
	reject("优先捕获")
})
promise.then(res => {
	return new Promise((resolve,reject) => {
		reject("也能捕获我")
	})
}).catch(err => {
	console.log(err)   // 优先捕获
	return "catch"
}).then(res => {
	console.log(res)   // catch
})

finally

finally方法不接受任何参数,无论promise的状态是fulfilled还是rejected,finally的代码都会执行

const promise = new Promise((resolve,reject) => {
	resolve("123")
})
promise.then(res => {
	console.log(res)    //123
}).finally(() => {
	console.log("我都会被执行")   //我都会被执行
})

Promise的类方法

类方法可以直接通过点操作符直接调用,比如:Promise.resolve()

resolve

resolve作为类方法的调用

const test = Promise.resolve("resolve作为类方法调用")
test.then(res => {
	console.log(res)   //resolve作为类方法调用
})

在Promise中resolve返回的是成功的结果,接下来,我们深入了解下resolve返回不同参数的情况

  • [1] resolve()传入 普通值/object,状态由pending--→fulfilled,返回传入的普通值/object
const test = new Promise((resolve,reject) => {
	resolve({name:"张三",age:18})
})

test.then(res => {
	console.log(res)     //{ name: '张三', age: 18 }
}) .catch(err => {
	console.log(err)
})
  • [2] resolve()传入 Promise,当前test的状态由传入的promise来决定,返回传入的promise的状态中的值
const promise = new Promise((resolve,reject) => {
	resolve('状态由我决定')
	// reject("错误")
})

const test = new Promise((resolve,reject) => {
	resolve(promise)
})

test.then(res => {
	console.log(res)      //状态由我决定
}) .catch(err => {
	console.log(err)
})
  • [3] resolve()传入 一个实现了then方法的对象,当前test的状态由传入的obj中then方法的状态决定,返回then方法的状态中的值
const test = new Promise((resolve,reject) => {
	const obj = {
		then: function(resolve,reject) {
			resolve("在这儿")
			// reject('err')
		}
	}
	resolve(obj)
})

test.then(res => {
	console.log(res)   //在这儿   
}) .catch(err => {
	console.log(err)
})

reject

reject作为类方法调用

const test = Promise.reject("reject类方法调用")
test.catch(err => {
	console.log(err)    //reject类方法调用
})

reject传入什么就返回什么,也就是说假如你传入的值是Promise,那么它将原封不动的将这个传入的Promise返回,不会做状态的移交。

all

Promise.all分为两种情况
一种是传入的所有promise都是fulfilled状态:传入的所有Promise状态都变为fulfilled后,all方法才能拿到结果。

const test1 = new Promise((resolve,reject) => {
	setTimeout(() => {
		resolve(111)
	},1000)
})
const test2 = new Promise((resolve,reject) => {
	setTimeout(() => {
		resolve(222)
	},2000)
})
const test3 = new Promise((resolve,reject) => {
	setTimeout(() => {
		resolve(333)
	},3000)
})

Promise.all([test1,test2,test3]).then(res => {
	console.log(res)    //[ 111, 222, 333 ]
})

另一种是传入的Promise存在rejected状态:传入的Promise只要有一个状态是rejected,那么整个Promise都是rejected。

const test1 = new Promise((resolve,reject) => {
	setTimeout(() => {
		resolve(111)
	},1000)
})
const test2 = new Promise((resolve,reject) => {
	setTimeout(() => {
		reject(222)
	},2000)
})
const test3 = new Promise((resolve,reject) => {
	setTimeout(() => {
		resolve(333)
	},3000)
})

Promise.all([test1,test2,test3]).catch(err => {
	console.log(err)   //222
})

all方法有一个不太友好的地方就是,如果其中有一个Promise处于rejected,那么其他处于fulfilled状态的结果会获取不到。

allSettled

allSettled 无论传入的Promise处于什么状态,他们的状态最终都会被返回。使用allSettled的Promise的状态一定是fulfilled

const test1 = new Promise((resolve,reject) => {
	setTimeout(() => {
		resolve(111)
	},1000)
})
const test2 = new Promise((resolve,reject) => {
	setTimeout(() => {
		reject(222)
	},2000)
})
const test3 = new Promise((resolve,reject) => {
	setTimeout(() => {
		reject(333)
	},3000)
})

Promise.allSettled([test1,test2,test3]).then(res => {
	console.log(res)   
})

打印结果:

[
  { status: 'fulfilled', value: 111 },
  { status: 'rejected', reason: 222 },
  { status: 'rejected', reason: 333 }
]

race

有一个Promise状态变成fulfilled/rejected,就结束;对于返回值来说,谁先fulfilled/rejected,就返回谁的结果。

const test1 = new Promise((resolve,reject) => {
	setTimeout(() => {
		resolve(111)
	},1000)
})
const test2 = new Promise((resolve,reject) => {
	setTimeout(() => {
		reject(222)
	},2000)
})
const test3 = new Promise((resolve,reject) => {
	setTimeout(() => {
		resolve(333)
	},3000)
})

Promise.race([test1,test2,test3]).then(res => {
	console.log(res)   // 111
})

any

对于race来说,无论是fulfilled还是rejected,谁先改变状态,就返回谁的值。但是any会等待状态变为fulfilled,有Promise变为fulfilled,就结束,返回这个fulfilled的结果;如果传入的Promise全是rejected,那么会返回一个错误

const test1 = new Promise((resolve,reject) => {
	setTimeout(() => {
		reject(111)
	},1000)
})
const test2 = new Promise((resolve,reject) => {
	setTimeout(() => {
		reject(222)
	},2000)
})
const test3 = new Promise((resolve,reject) => {
	setTimeout(() => {
		resolve(333)
	},3000)
})

Promise.any([test1,test2,test3]).then(res => {
	console.log(res)    // 333
})

传入的Promise全是rejected

const test1 = new Promise((resolve,reject) => {
	setTimeout(() => {
		reject('err1')
	},1000)
})
const test2 = new Promise((resolve,reject) => {
	setTimeout(() => {
		reject('err2')
	},2000)
})
const test3 = new Promise((resolve,reject) => {
	setTimeout(() => {
		reject('err3')
	},3000)
})

Promise.any([test1,test2,test3]).then(res => {
	console.log(res)   
}).catch(err => {
	console.log(err.errors)   //['err1', 'err2', 'err3']
})