JS中Promise的使用

182 阅读8分钟

异步任务的处理

这里我从一个实际的例子来作为切入点,我们调用一个函数,这个函数中发送网络请求(我们可以用定时器来模拟),如果发送网络请求成功了,那么告知调用者发送成功,并且将相关数据返回过去,如果发送网络请求失败了,那么告知调用者发送失败,并且告知错误信息。

function requestData(url,successCallback,failtureCallback){
  setInterval(() => {
    if(url === 'harry'){
      let names = ["abc","ccc","cba"]
      //将names通过回调函数返回
      successCallback(names)
    }else{
      let errMessage = '请求失败,url错误'
      failtureCallback(errMessage)
    }
  }, 3000);
}

requestData("harry",res => {
  console.log(res);
},err => {
  console.log(err);
})

这样做有许多的缺点。

  1. 如果我们自己封装的requestData,那么我们在封装的时候必须要自己设计好callback名称,并且使用好
  2. 如果我们使用的是别人封装的requestData,或者一些第三方库,那么我们必须去看别人的源码或者文档,才知道这个函数需要怎么获取到结果。

使用Promise就能很好的解决上述的问题,Promise规范好了所有的代码编写逻辑。 用伪代码来描述,就是这样。

function requestData2(){
  return "承诺"
}

const chengnuo = requestData()

什么是Promise呢?

Promise是一个类,可以翻译成 承诺、许诺 、期约(红宝书里是这么翻译的,当时看到让我非常的疑惑)。当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个Promise的对象。

Promise的基本使用

Promise以及executor函数

//Promise是一个构造函数,需要用new创建
const promise = new Promise(()=>{ //传入的参数是个回调函数,并且传递进去的同时,立刻执行,这个函数叫做excutor

})

executor函数中的resolve以及reject

const promise = new Promise((resolve,reject)=>{ //excutor函数中还有两个函数,resolve,reject
  // console.log('promise传入的函数被执行了');
  resolve()
}) 

promise.then(()=>{ //then方法传入的回调函数,会在Promise执行resolve函数时,被回调

}) //成功,在then中再传入一个回调函数,并且这个回调函数会立刻执行。

.catch(()=>{ //catch方法传入的回调函数,会在Promise执行reject函数时,被回调
  
})

在异步请求中使用Promise

我们使用Promise改造我们的异步请求

function requestData(url){
  return new Promise((resolve,reject)=>{
    setInterval(() => {
      if(url === 'harry'){
        let names = ["abc","ccc","cba"]
        resolve(names)
      }else{
        let errMessage = '请求失败,url错误'
        reject(errMessage)
      }
    }, 3000);
  })

}

//then方法传入的回调函数两个回调函数
//>第一个回调函数,会Promise指向resolve函数时候,被回调
//>第二个回调函数,会在Promise指向reject函数时候,被回调
const promise = requestData("harry")
promise.then(res=>{
  console.log('请求成功');
  console.log(res);
}).catch(err =>{
  console.log('请求失败');
  console.log(err);
})
//或者这样
// promise.then(res=>{
//   console.log('请求成功');
//   console.log(res);
// },err=>{
//   console.log('请求失败');
//   console.log(err);
// })

Promise的三种状态

Promise有三种状态,pending(悬而未决的状态),fullfilled(已敲定状态)和rejected(已拒绝状态),具体看下面的代码。

new Promise((resolve,reject)=>{  //excutor中给promise划分状态
  console.log('------');
  resolve()      //pending阶段(待定状态)(悬而未决的)
  //reject
}).then(res => {
  console.log("res:",res);   //fullfilled/resolved阶段(已敲定状态)
                              //promise状态一旦没确定,不能更改 比如这里不能reject()
},err => {
  console.log("err:",err);    //rejected(已拒绝状态)
})

resolve参数

resolve函数中的参数有三种情况

  1. 普通的值或者对象
  2. 传入Promise
  3. 传入一个对象并且这个对象有then方法。

第一种情况,这种参数会直接来到promise的then处。

const promise = new Promise((resolve,reject)=>{
  // resolve("aaa")
  resolve({name:'harry'})
})

promise.then(res=>{
  // console.log("res:",res); //res: aaa
  console.log("res:",res); //res: { name: 'harry' }
})

第二种情况,传入Promise。Promise的状态这时会由传入的promise状态来决定。

const promise = new Promise((resolve,reject)=>{
  // resolve({name:'harry'}) //** 
  reject("error message")
})

new Promise((resolve,reject)=>{
  resolve(promise)
}).then(res => {
  console.log(res); //{ name: 'harry' }
}).catch(err =>{
  console.log(err); //error message
})

第三种情况。如果传入一个对象,并且这个对象有实现then方法,那么Promise会指向该then方法,并且由该then方法决定后续状态(thenable)。

const promise = new Promise((resolve,reject) => {
  const obj = {
    then(resolve,reject){
      resolve("resolve message")
    }
  }
  resolve(obj)
})

promise.then(res => {
  console.log("res:",res); //res: resolve message
},err => {
  console.log("err:",err);
})

Promise对象方法

查看Promise的对象方法

console.log(Object.getOwnPropertyDescriptors(Promise.prototype));

image.png

then方法

1.同一个Promise可以被多次调用then方法

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

promise.then(res => {
  console.log("res1:",res);
})

promise.then(res => {
  console.log("res2:",res);
})

promise.then(res => {
  console.log("res3:",res);
})
  1. then方法传入的回调函数是可以有返回值的 then方法本身也是有返回值的,它的返回值是Promise

1> 如果我们返回的是一个普通值,那么这个普通的值会被作为一个新的Promise的resolve值

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

promise.then(res =>{
  return "aaa"
}).then((res)=>{
  console.log(res); //aaa
})

// 这其实等价于
return new Promise(resolve =>{
  resolve("aaa")
})

2> 如果我们返回的是一个Promise

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

promise.then(res=>{
  return new Promise((resolve,reject) => {
    setTimeout(()=>{
      resolve(11111)
    },3000)
  })
}).then(res=>{
  console.log(res); //3秒后打印  11111 
})

3> 如果我们返回的是一个对象,并且该对象实现了thenable

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

promise.then(res=>{
  return {
    then(resolve,rejcet){
      resolve(222)
    }
  }
}).then(res=>{
  console.log("res:",res); //res: 222
})

catch方法

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

promise.then(undefined,err =>{
  console.log('err:',err);
  console.log('------------------------');
})

throw new Error()

const promise = new Promise((resolve,reject)=>{
  throw new Error("rejected status") //这和reject一样的 Error: rejected status 但是会把所有堆栈调用信息都打印出来了
})

promise.then(undefined,err =>{
  console.log('err:',err);
  console.log('------------------------');
})

通过catch方法来传入错误的捕获

const promise = new Promise((resolve,reject)=>{
  reject("rejected status") 
})

//2.通过catch方法来传入错误(拒绝)的捕获
//不过这种规则并不符合promise/a+规范

promise.catch(err => {
  console.log("err:",err); //err: rejected status
})

拒绝捕获的问题,这种写法报错的原因。

const promise = new Promise((resolve,reject)=>{
  reject("rejected status") 
})


promise.then(res => { //相当于两次独立的调用,它们之间没关系

})
promise.catch(err => { //相当于两次独立的调用

})

catch返回值问题

const promise = new Promise((resolve,reject)=>{
  reject("rejected status") 
})

promise.then(res => {
  console.log("res:",res);
}).catch(err => {
  console.log("err:",err);
  return "catch return value" //也是new了一个Promise
}).then(res => {
  console.log(res);
}).catch(err => {
  console.log(err); //catch return value
})

finally方法

// es9(2018)新增的一个特性,表示无论promise对象是fullfilled还是rejected最终都会被执行的一段代码
//finally方法是不接受参数的
const promise = new Promise((resolve,reject) => {
  // resolve("resolve message")
  reject("error message")
})

promise.then(res => {
  console.log("res:",res);
}).catch(err => {
  console.log("err:",err);

}).finally(()=>{
  console.log('finally code excute');
})

类方法

resolve

resolve用法前文说过了,没什么好解释的。传入普通对象,promise,对象中实现then方法都跟前面的规则一样。

// 类方法Promise.resolve
// 1.普通的值
const promise = Promise.resolve({name:'harry'}) 
//相当于
const promise2 = new Promise((resolve,reject) => {
  resolve({name:'harry'})
})

promise.then(res => {
  console.log("res:",res);
})

reject

reject注意无论传入什么值,都是一样的。他不会区分几种情况(普通值,promise等等)

// const promise = Promise.reject("reject message")
//相当于
// const promise2 = new Promise((resolve,reject)=>{
//   reject("reject message")
// })

//reject注意无论传入什么值,都是一样的。他不会区分几种情况(普通值,promise等等)。
const promise = Promise.reject({
  then(resolve,reject){
    resolve("1111")
  }   
})


promise.then(res => {
  console.log("res:",res);
}).catch(err =>{
  console.log("err:",err); //err: { then: [Function: then] } 它没有帮我调用,直接把值给了catch
})

all方法

//创建多个Promise

const p1 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("1111")
  }, 1000);
})

const p2 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("2222")
  }, 2000);
})

const p3 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("3333")
    // reject("3333")
  }, 3000);
})

//需求:所有的Promise都变成fullfilled时,再拿到结果; all 传入数组
//意外:在拿到所有结果之前,有一个promise变成rejected,那么整个promise是rejected了。就不会执行then了,会执行catch
Promise.all([p1,p2,p3,"aaa"]).then(res =>{
  console.log(res); //等待三秒后返回一个数组,数组值是[ '1111', '2222', '3333', 'aaa' ]
}).catch(err => {
  console.log('err:',err); //err: 3333
})

如果我有另外一个需求,不希望有一个reject其他的Promise都中断。那就得用到allSettle

allSettle

//创建多个Promise

const p1 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("1111")
  }, 1000);
})

const p2 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("2222")
  }, 2000);
})

const p3 = new Promise((resolve,reject) => {
  setTimeout(() => {
    // resolve("3333")
    reject("3333")
  }, 3000);
})



//另外一个需求,不希望有一个reject其他的都中断

//allSettled
Promise.allSettled([p1,p2,p3,"aaa"]).then(res =>{
  console.log(res); 
  /** 返回的结果
   * [
        { status: 'fulfilled', value: '1111' },
        { status: 'fulfilled', value: '2222' },
        { status: 'rejected', reason: '3333' },
        { status: 'fulfilled', value: 'aaa' }
      ]
   */
}).catch(err => {
  console.log('err:',err); 
})

race

//race竞赛
//创建多个Promise

const p1 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("1111")
  }, 1000);
})

const p2 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("2222")
  }, 2000);
})

const p3 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("3333")
    // reject("3333")
  }, 3000);
})



//竞赛

//race
//只要有一个Promise变成fullfilled状态,那么就结束
//这三个都没结果突然有个reject,这时候拿到的就是reject的结果。
Promise.race([p1,p2,p3]).then(res =>{
  console.log('res:',res);  //res: 1111
  
}).catch(err => {
  console.log('err:',err); 
})

any

//与race类似,但是它会至少等到一个状态fullfilled
//这个node 14不支持,需要用浏览器,是es11才新出的

const p1 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("1111")
  }, 1000);
})

const p2 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("2222")
  }, 2000);
})

const p3 = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve("3333")
    // reject("3333")
  }, 3000);
})



Promise.any([p1,p2,p3]).then(res =>{
  console.log('res:',res);  //res: 1111
  
}).catch(err => {
  console.log('err:',err); 
})