Promise基础知识

132 阅读8分钟

前言

该文章记录的是一些关于Promise的基础知识,后续将会慢慢添加完Promise相关的所有知识,所有内容均从网上整理而来,加上自己的一些理解,方便在工作中使用。

一. Promise

1. Promise理解

  • 抽象表达
    • Promise是ES6规范中解决异步编程的新方案
    • 旧方案是单纯使用回调函数
  • 具体表达
    • 从语法上来说,Promise是一个构造函数
    • 从功能上来说,Promise对象用来封装一个异步操作并可以获取其成功/失败的结果

2. Promise优点

  • 指定回调函数的方式更加灵活
    • Promise:启动异步任务-->返回promise对象-->给promise对象绑定回调函数(可以指定多个)
    • 旧方案必须在启动异步任务之前指定回调函数
  • 支持链式调用,解决回调地狱问题

3. Promise的状态

  • 实例对象中的一个属性 [PromiseState]
    • pending 未决定的
    • resolved/fulfilled 成功状态
    • rejected 失败状态
  • 状态改变:状态改变不可逆,且只有两种改变方式
    • pending --> resolved
    • pending --> rejected

4. Promise对象的值

  • 实例对象中的属性 [PromiseResult],保存着对象成功/失败的结果
  • resolve/reject方法可以修改其值

5. Promise的工作流程

promises.png

二、Promise的API

1. Promise构造函数:Promise(excutor){}

  • excutor函数:执行器--> (resolve,reject)=>{}
  • resolve函数: 内部定义成功时调用,可以传参resolve(xxx)
  • reject函数: 内部定义失败时调用,可以传参reject(xxx)
  • 注意:excutor函数是同步调用
     let p = new Promise((resolve, reject) => {
       //excutor函数是同步调用的
       console.log(111)
     })
     console.log(222)
     //先输出 111,再输出222
    

2. Promise实例方法

2-1. Promise.prototype.then(onResolved,onRejected)

  • onResolved函数:成功时调用--> (res)=>{}
  • onRejected函数:失败时调用--> (err)=>{}

then方法返回的是一个新的promise对象

2-2. Promise.prototype.catch(onRejected)

  • onRejected函数:失败时调用--> (err)=>{}
 //我的个人习惯是then方法只用调用onResolved函数,catch调用onRejected函数,将成功/失败分开
 let p = new Promise((resolve, reject) => {
   if(xxx){
       reject('失败了')
   }else{
       resolve('成功了')
   }
 })
 p.then(res => {
   console.log('成功', res)
 }).catch(err => {
   console.log('失败', err)
 })

3. Promise静态方法

3-1. Promise.resolve()

  • 作用:可以将一个值封装,返回一个成功/失败的promise对象
  • 如果传递的参数为 非Promise对象,则返回的结果为成功状态的promise对象
  • 如果传递的参数为 Promise对象,则参数的结果状态决定了resolve返回的结果状态
    let p = Promise.resolve(100)
    console.log(p) //输出:Promise {<fulfilled>: 100}
    
    let p2 = Promise.resolve(new Promise((resolve, reject) => {
      reject('err')
    }))
    console.log(p2) //输出: Promise {<rejected>: 'err'}
    

3-2. Promise.reject()

  • 作用:可以将一个值封装,返回一个失败的promise对象
  • 不管传入的是不是Promise对象,则返回的结果都是失败状态的promise对象
    let p = Promise.reject(100)
    console.log(p) //输出:Promise {<rejected>: 100}
    
    let p2 = Promise.reject(new Promise((resolve, reject) => {
      resolve('err')
    }))
    console.log(p2) //输出: Promise {<rejected>: Promise}
    

3-3. Promise.all([xxx])

  • 作用:数组包裹n个promise对象,返回一个新的promise对象
  • 只有所有的promise状态为成功,新返回的promise对象状态才为成功,否则都是失败状态
  • 一般用来多个接口同时调用时,有一个接口调用失败,都返回失败
    let p = Promise.resolve(100) //成功状态
    let p2 = new Promise((resolve, reject) => {
      resolve('res')
    }) //成功状态
    let p3 = Promise.resolve('hello') //成功状态
    
    let result = Promise.all([p, p2, p3])
    console.log(result) //输出:Promise {<pending>} 成功状态
    result.then(res => {
      console.log(res) //输出:[100, 'res', 'hello']
    })
    

3-4. Promise.race([xxx])

  • 作用:数组包裹n个promise对象,返回一个新的promise对象
  • 通俗来说,就是n个promise对象谁先有结果,不管成功/失败,就采用第一个 promise 的值作为返回值
    let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('one')
      }, 1000);
    })
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('two')
      }, 500);
    })
    
    let result = Promise.race([p1, p2])
    console.log(result) //输出:Promise {<pending>} 成功状态
    result.then(res => {
      console.log(res) //输出:two
    })
    

三、Promise关键问题

1. 修改Promise对象状态的方法

  • resolve() --> pending=>fulfilled
  • reject() --> pending=>rejected
  • throw xxx 抛出错误 --> pending=>rejected

2. 一个promise指定多个成功/失败回调函数,当状态改变,都会调用

let p = new Promise((resolve, reject) => {
  resolve('ok')
});
p.then(res => {
  console.log(1, res)
})
p.then(res => {
  console.log(2, res)
})
//输出:1 'ok'
//输出:2 'ok'

3. 改变状态和指定回调的顺序问题(了解)

  • 都有可能,正常情况是先指定回调函数,再改变状态;也可以先改变状态,再指定回调

    注意:指定回调函数和执行回调函数是不一样的

  • 先改变状态,再指定回调
    • 在执行器中直接调用resolve( )/reject( ),同步任务中调用
    • 延迟更长的时间调用then( )
  • 什么时候获得数据
    • 不管then方法是否先指定了回调函数,必须等状态改变后,才能执行回调函数获得数据
  let p = new Promise((resolve, reject) => {
    //执行器中如果是异步任务中调用resolve/reject,那么then方法先指定回调,状态后改变
    setTimeout(() => {
      resolve('ok')
    }, 1000);
    //执行器中如果是同步任务调用resolve/reject,就是先改变状态,then方法再指定回调
    resolve('ok')
  });
  
  //then方法指定了回调函数,但是也得等状态改变,才能执行回调函数,才能获得数据
  p.then(res => {
    console.log(1, res)
  },err=>{
    console.log(1, err)
  })

4. then( )返回的新promise对象的结果状态由什么决定?

由then方法指定的回调函数执行结果决定

  • 如果抛出错误,返回的新promise对象状态为rejected
  • 如果回调函数返回的是非promise对象,返回的新promise对象状态为resolved
  • 如果回调函数返回的是promise对象,返回的新promise对象状态由该promise对象的状态决定
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('ok')
      }, 1000);
    });
    
    let result = p.then(res => {
      //1.throw,状态结果为rejected
      // throw '抛出错误'
    
      //2.非promise对象,状态结果为fulfilled
      // return 123
    
      //3.promise对象,由此状态结果决定
      // return Promise.resolve('123') //状态结果fulfilled 
      return Promise.reject('123') //状态结果rejected 
    
    })
    console.log(result)
    

5. promise串联多个任务

promise的then()方法返回的是一个新的promise对象,因此可以使用链式调用,通过then方法的链式调用,串联多个同步/异步任务

let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok')
  }, 1000);
});

//先输出 ok ,然后输出success
p.then(res => {
  console.log(res)
  return Promise.resolve('success')
}).then(res => {
  console.log(res)
})

//先输出 ok ,然后输出undefined
p.then(res => {
  console.log(res)
}).then(res => {
  console.log(res)
})
//因为第一个then方法的回调函数的返回值是undefined,第二个then方法获取到的值就是undefined

6. promise链式调用异常穿透

  • 当使用then链式调用时,可以在最后指定失败的回调
  • 前面有任何出了异常,都会传递到最后的失败回调中处理
      let p = new Promise((resolve) => {
        resolve('one')
      })
    
      p.then(res => {
        console.log('two')
        return Promise.reject('失败')
      }).then(res => {
        console.log('three')
      }).then(res => {
        console.log('four')
      }).catch(err => {
        console.log(err)
      })
      //输出 two 失败
      //当promise链式调用时,其中有错误抛出或则是失败,后面的then不会执行,直接传递到catch指定的回调函数
    

7. 中断promise链式调用

  • 当使用promise链式调用时,需要中断,不再执行后面回调函数,在回调函数中返回一个pending状态的promise对象
    let p = new Promise((resolve) => {
     resolve('one')
    })
    
    p.then(res => {
     console.log('two')
     return new Promise(() => {
    
     })
     //回调函数中返回一个pending状态的promise对象,后续就会中断
    }).then(res => {
     console.log('three')
    }).then(res => {
     console.log('four')
    }).catch(err => {
     console.log(err)
    })
    

四、async和await

1. async函数

  • async函数返回值为promise对象,和then方法返回promise对象一致
  • 返回得promise对象的结果由async函数的返回值决定
    1. 如果返回值是非promise类型的数据,则async函数返回是成功状态的promise对象
    2. 如果返回值是promise对象,则该promise对象状态结果决定async函数返回的promise对象
    3. 如果是抛出错误,async函数返回的promise对象是失败状态
    async function main(){
      //1.如果返回值是非promise类型的数据,则async函数返回是成功状态的promise对象
      //return 100
    
      //2.如果返回值是promise对象,则该promise对象状态结果决定async函数返回的promise对象
      //return Promise.resolve('成功')  
      //return Promise.reject('失败')
    
      //3.如果是抛出错误,async函数返回的promise对象是失败状态
      // throw '抛出错误'
    }
    
    let result = main()
    

2. await表达式

  • await表达式右侧一般是promise对象,也可以是其他值
  • 如果右侧是promise对象,await返回的是promise成功的值,如果是失败状态,直接抛出错误
  • 如果右侧为非promise对象,则直接返回该值
  • 注意:
    • await必须写在async函数中,但是async函数可以没有await
    • 如果await右侧是失败状态的promise对象,则需要try···catch捕获抛出的错误
async function main() {
   //1.如果await右侧是非promise类型的数据,直接返回该值
   let res = await 123
   console.log(res) //输出123

   //2.如果await右侧是promise对象
   //如果是成功状态的promise对象
   let res1 = await Promise.resolve('成功')
   console.log(res1) //输出 成功

   //如果是失败状态的promise对象
   try {
     let res2 = await Promise.reject('失败')
   } catch (error) {
     console.log(error) //输出失败
   }
}

let result = main()

3. async和await结合使用

//我的注意使用途径就是调接口
async function main() {
  try {
    let res = await 调接口
    let res2 = await 调接口
    //成功了,处理数据
  } catch (error) {
    //失败了,处理数据
  }
}
main()