ES6 之前的异步解决方案
在了解promise前先了解一下ES5之前是怎么处理异步任务的
用一个网络请求的例子
函数中发送网络请求;
发送网络请求成功了,那么告知调用者发送成功,并且将相关数据返回过去;
发送网络请求失败了,那么告知调用者发送失败,并且告知错误信息;
function requestDate(value,successCallback,failureCallback){
setTimout(() => {
if(value){
successCallback("获取成功接收到的数据")
}else{
failureCallback("错误信息")
}
})
}
这代码看起来也不复杂,但当我们需要发送多个异步请求,并且每个请求之间需要相互依赖,那时,我们只能 以嵌套方式来解决,从而就形成回调地狱
回调地狱指的是多个函数内嵌而导致的一层套一层的函数结构,阅读效果差,会导致多种问题,在异步操作需要变更时,需要大规模的代码重构。
而promise的出现,就是为了解决原有技术上的痛点
Promise是什么?
抽象理解Promise 是 JS 中进行异步编程的新的解决方案
具体理解从语法上来说:promise 是一个构造函数,给我们调用者一个承诺,承诺返回一个结果
promise 的基本使用
在通过new Promise(),我们需要传入一个回调函数,我们称之为executor
- 这个回调函数会被立即执行(同步),并且给传入另外两个回调函数resolve、reject;
- 当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数;
- 当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数;
const promise = new Promise((resolve,reject)=>{
//resolve();
//reject();
})
promise.then((res)=>{...}).catch((err)=>{...})
executor(执行器)
executor是在创建Promise时需要传入的一个回调函数,这个回调函数是同步的会被立即执行,并且传入两个参数。
在executor函数中promise有三种状态:
- 在未等待状态改变之前属于 pending 状态
- 在执行了resolve,则属于成功 fulfilled 状态
- 当执行了reject,则属于失败 reject 状态
注意点:promise的状态一旦确定下来,无论是成功还是失败,都会有一个结果数据,无法更改,在执行了resolve之后再调用reject,此时状态是不会变更的。
resolve参数的区别
传入一个普通的值或者对象,那么这个值会作为then回调的参数;
new Promise((resolve,reject)=>{
resolve({})//(Number|String|Object|undefinde)
}).then(res =>{
console.log("res":res)//"res":{}
})
传入的是一个Promise,那么这个新Promise会决定原Promise的状态;
new Promise((resolve,reject) =>{
resolve(new Promise(resolve,reject) =>{
setTimeout(() =>{
resolve("返回promise")
},1000)
})
}).then(res =>{
console.log("res:",res)// "res:",返回promise
})
传入的是一个对象,并且这个对象有实现then方法,那么会执行该then方法,并且根据 then方法的结果来决定Promise的状态;
new Promise((resolve,reject)=>{
resolve({
then:function(resolve,reject){
resolve("返回promise")
}
})
}).then(res =>{
console.log("res:",res)// "res:",返回promise
})
then方法
then方法是Promise原型对象上的一个方法,根据上一个成功的结果执行,接受两个参数:
- 第一个参数是fulfilled的回调函数
- 第二个参数是preject的回调函数
new Promise((resolve,reject)=>{
resolve()
}).then(res =>{
console.log(res)
},err =>{
console.log(err)
})
返回值
then方法本身是有返回值的,它的返回值是一个Promise,所以我们可以进行链式调用
当then方法中的回调函数返回一个结果时,那么它处于fulfilled状态,并且会将结果作为resolve的参数;
当then方法抛出一个异常时,那么它处于reject状态;
返回值是一个普通的值
//当返回的是一个普通值, 那么这个普通的值被作为一个新的Promise的resolve值
promise.then(res => {
return 100; //(数值/字符串/普通对象/undefined)
}).then(res => {
console.log("res:", res)
return 120;
})
返回值是一个Promise
// 当返回的是一个Promise
promise.then(res => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 3000)
})
}).then(res => {
console.log("res:", res)
})
返回值一个包含then方法的值
//当返回的是一个对象, 并且该对象实现了thenable
promise.then(res => {
return {
then: function(resolve, reject) {
resolve(100)
}
}
}).then(res => {
console.log("res:", res)
})
catch
catch等同于then第二个参数
promise.catch(err => {
console.log("err:", err)
})
//等同于
promise.then(null,()=>{})
返回值catch方法也是会返回一个Promise对象的,所以catch方法后面也可以调用then方法或者catch方法
new Promise((resolve,reject)=>{
reject()
}).then(res =>{
console.log("res:",res)
}).catch(err =>{
console.log("err:",err)//err: 1000
}).then(res => {
console.log("res0:",res)//res0: undefined
}).catch(err =>{
console.log("err0:",err)
})
//抛出错误
new Promise((resolve,reject)=>{
reject()
}).then(res =>{
console.log("res:",res)
}).catch(err =>{
console.log("err:",err)//err: 1000
return new Error("try err")
}).then(res => {
console.log("res0:",res)
}).catch(err =>{
console.log("err0:",err)//res0: Error: try err
})
finally
finally是在ES9(ES2018)中新增的一个特性:表示无论Promise对象无论变成fulfilled还是reject状态,最终都会被执行的代码
finally方法是不接收参数的,因为无论前面是fulfilled状态,还是reject状态,它都会执行
new Promise((resolve,reject)=>{
reject(123)
}).then(res =>{
console.log("res:",res)
}).catch(err =>{
console.log("err:",err)
}).finally(()=>{
console.log("finally")
})
resolve
resolve方法是Promise类直接调用,Promise.resolve();
同样参数拥有
- 返回一个普通的值;
- 返回一个Promise;
- 返回一个thenable值;
Promise.resolve()
//等同
new Promise((resolve,reject)=>{
resolve()
})
reject
reject方法Promise类直接调用,Promise.reject();
Promise.reject()
//等同
new Promise((resolve,reject)=>{
reject()
})
all
作用:将多个Promise包裹在一起形成一个新的Promise
新的Promise状态由包裹的所有Promise共同决定:
- 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组;
- 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数;
all()方法Promise类直接调用,Promise.all;
const p0 = new Promise((resolve,reject)=>{...})
const p1 = new Promise((resolve,reject)=>{...})
const p2 = new Promise((resolve,reject)=>{...})
const p3 = Promise.all([p0,p1,p2])
.then(res =>{
console.log(res)
})
.catch(err =>{
console.log(err)
})
allSettled
all方法有一个缺陷:当有其中一个Promise变成reject状态时,新Promise就会立即变成对应的reject状态;
但对于resolved,以及依然处于pending状态的Promise,我们是获取不到对应的结果的;
在ES11(ES2020)中,添加了新的API Promise.allSettled:
- 该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是reject时,才会有最终的状态;
- 并且这个Promise的结果一定是fulfilled的;
const p0 = new Promise((resolve,reject)=>{resolve(123)})
const p1 = new Promise((resolve,reject)=>{resolve(true)})
const p2 = new Promise((resolve,reject)=>{reject(false)})
Promise.allSettled([p0,p1,p2])
.then(res =>{ console.log(res) })
.catch(err =>{ console.log(err) })
//pallSettled的结果是一个数组,数组中存放着每一个Promise的结果,并且是对应一个对象的;
//这个对象中包含status状态,以及对应的value值;
(3) [{…}, {…}, {…}]
0: {status: 'fulfilled', value: 123}
1: {status: 'fulfilled', value: true}
2: {status: 'rejected', reason: false}
race
race表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果
const p0 = new Promise((resolve,reject)=>{resolve(123)})
const p1 = new Promise((resolve,reject)=>{resolve(true)})
const p2 = new Promise((resolve,reject)=>{reject(false)})
Promise.race([p0,p1,p2])
.then(res =>{
console.log(res)
})
.catch(err =>{
console.log(err)
})
any
any方法是ES12中新增的方法,和race方法是类似的:
- any方法会等到一个fulfilled状态,才会决定新Promise的状态;
- 如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态;
- 如果所有的Promise都是reject的,那么会报一个AggregateError的错误。
const p0 = new Promise((resolve,reject)=>{reject(123)})
const p1 = new Promise((resolve,reject)=>{reject(true)})
const p2 = new Promise((resolve,reject)=>{reject(false)})
Promise.any([p0,p1,p2])
.then(res =>{
console.log(res)
})
.catch(err =>{
console.log(err) //AggregateError: All promises were rejected
})