一、基本概念
    (1)promise是javascript提供的标准化异步管理方案。总体思想是,等待某些任务执行,不直接返回结果,而 是返回一个’承诺’,可以根据这个对象的状态选择合适的时机进行后续操作。
    (2)传统回调、promise、async和await比较。
    a.回调函数面临着复杂嵌套的困境,看起来复杂,给开发和维护制造了麻烦。
    b.promise比起传统的回调,使用链式操作更人性化,而且提供了方便的api。
    c.async和await,利用promise直接,可以将链式调用彻底改为同步写法,异步操作最人性化的写法。

    二、promise核心用法
    promise的核心在于状态,实例创建后的状态为pending,之后状态可以转变为fullfilled(调用resolve方法)和rejected(调用reject方法或者抛出异常),状态不可逆转。想要在promise为fullfilled时做后续操作,可以调用then方法传入第一个参数,promise为rejected时可以给then方法传入第二个函数参数,
    使用构造函数Promise创建一个实例,接收一个excutor函数
    excutor: javascript会将两个状态改变函数resolve,reject传入excutor作为参数,执行excutor时,可以选择调用resolve方法将promise状态改为resolved,可以调用reject函数或者抛出异常将promise状态改为rejected。如果如果没有处理rejected的promise,promise会抛出异常( uncaught (in promise))。
    resolve和reject中可以返回值作为then和catch的参数函数的参数。

    1. let myPromise = new Promise((resolve, reject)=>{
    2. setTimeout(()=>{
    3. resolve('resolved')
    4. },2000)
    5. });
    6. //无需在promise创建时就调用then和catch方法,可以自己选择时机进行后续处理
    7. myPromise.then((res)=>{console.log(res)},(error)=>{console.log(error)})

    在excutor中抛出异常相等于reject( new Error() )
    promise状态改变以后,抛出的异常不会被捕获因为promise的状态一旦改变就永久保持该状态

    1. let myPromise = new Promise((resolve, reject)=>{
    2. throw new Error('rejected error')
    3. //等价于
    4. reject(new Error('rejected error'))
    5. //但是后面的reject函数不会执行,因为状态只能改变一次
    6. })

    resolve方法处理一个promise时:当前promise状态由resolve处理的promise决定,在参数promise状态改变后当前promise状态由pending变为参数promise状态。

    1. let p1 = new Promise((resolve, reject)=>{
    2. setTimeout(()=>{
    3. reject(new Error('p1 rejected'))
    4. },5000)
    5. }),
    6. p2 = new Promise((resolve)=>{
    7. setTimeout(()=>{
    8. //当调用resolve处理另一个promise时,当前promise需要等待前一个promise状态改变,
    9. //且状态由参数promise决定
    10. resolve(p1)
    11. },1000)
    12. });
    13. p2
    14. .then((res)=>{console.log('fullfilled', res)})
    15. .catch((error)=>{console.log('rejected', error)}) // 'p1 rejected'

    三、Promise.prototype
    (1)Promise.prototype.then - promise状态改变后可执行的函数
    promise.prototype.then的第一个参数函数用作promise为fullfilled状态的回调方法
    promise.prototype.then的第二个参数函数用作promise为rejected状态捕获异常(等价于 Promise.prototype.catch)的回调方法。
    Promise.prototype.then执行后,会默认返回新的一个状态为fullfilled的promise也可以返回一个自定义的promise实例。
    (2)Promise.prototype.catch
    用作rejected状态的promise回调处理,也可用于捕获promise内部异常。promise的异常有类似冒泡的行为机制,promise内部错误(excutor和then方法中的错误)会向后传递,所以promise链式调用时catch方法可以捕获前面的错误
    所以更推荐使用.catch方法,then方法第二个参数函数只能捕获当前promise错误,.catch方法可以捕获前面的promise错误。
    promise内部错误不会影响外部代码执行。
    catch和then返回值一样,默认返回fullfilled的promise,或者自己返回新的promise。

    1. const promise = new Promise(function (resolve, reject) {
    2. resolve('ok');
    3. setTimeout(function () {
    4. console.log('timeout')
    5. let mypromise = new Promise((resolve)=>{
    6. console.log('promise in timeout')
    7. resolve()
    8. }).then((res)=>{
    9. console.log('microtask in timeOut')
    10. })
    11. throw new Error('test')
    12. }, 0)
    13. });
    14. promise.then(function (value) { console.log(value) }).catch((error)=>{console.log(error)});
    15. //setTimeout是在下一个宏任务,promise在上一轮宏任务中已经执行完毕,
    16. //所以setTimeout是在下一个事件轮询中,promise外部抛出的异常
    17. //所以catch方法捕获不到异常

    catch也可以链式调用

    1. let myPromise = new Promise((resolve, reject)=>{
    2. reject(x + 2)
    3. });
    4. myPromise.catch((error)=>{
    5. console.log(error, 'first catch')
    6. reject( y + 2)
    7. }).catch((error)=>{
    8. console.log(error, 'seconde catch')
    9. })

    (3)Promise.prototype.finally
    无论promise状态是fullfilled还是rejected,finally都会执行,且finally回调参数不接受任何值,所以finally的执行与promise状态和计算结果没有关系。

    四、使用promise封装ajax请求函数

    1. //基本参数: 方法 string、url string、headers Array
    2. function promiseForAjax(method,url,params,headers){
    3. return new Promise((resolve, reject)=>{
    4. //验证参数
    5. if(!method || !url){
    6. //如果参数验证不通过,抛出异常也会改变promise状态
    7. throw new Error('请传完整参数')
    8. }
    9. let request = new XMLAndHttpRequest();
    10. request.open(method, url, true);
    11. //创建readyState监听函数
    12. function readyStateListener(request){
    13. if(request.readyState !== 4) return
    14. let response = request.responseText;
    15. if(request.status === 200){
    16. resolve(response)
    17. }else(
    18. reject(response)
    19. )
    20. }
    21. //绑定状态监听函数
    22. request.readyStateChange = readyStateListener
    23. //如果有header,设置header
    24. if(headers && Object.toString.call(headers) === '[obejct Object]'){
    25. let headerArr = Object.entries(headers)
    26. headerArr.forEach(([key,value])=>{
    27. request.setRequestHeader(key,value)
    28. })
    29. }
    30. //计算参数字符串
    31. if(params){
    32. let paramsArr = Object.entries(params),
    33. paramStr = '';
    34. paramsArr = paramsArr.reduce((accumulotar,currentArr)=>{
    35. accumulotar.push(currentArr.join(','))
    36. },[]);
    37. paramStr = paramsArr.join('&')
    38. }
    39. //发送请求
    40. request.send(paramStr) //此处可以传参数 'key=value&key=value'形式
    41. })
    42. }

    五、Promise异步管理方法
    (1)Promise.all,用来管理多个promise实例,返回一个新的promise。
    直到所有promise状态改变后,返回的实例状态才会改变:如果管理的promise都为fullfilled,返回的promise状态才为fullfilled,不然就为rejected。
    参数:具有iterator接口的集合,如果元素不是promise,会先调用Promise.resolve来包装。
    返回值:
    如果是fullfilled,那么返回管理的promise返回值集合,如果是rejected,返回第一个rejected的promise返回值。
    在需要等待两个不同后台接口都返回数据后的情况,用promise.all就很方便。
    (2)Promise.race,管理多个promise,只要一个promise状态改变,返回的promise状态就改变。
    参数同Promise.all
    返回值,第一个改变状态的promise返回值。

    六、快速创建promise实例
    (1)快速创建一个resolved的promise - Promise.resolve
    根据传入的参数类别,返回一个resolved的promise
    1、传入一个promise,原封不动地返回这个实例
    2、如果传入一个有then方法的对象,创建一个promise,并执行then方法。

    1. let thenable = {
    2. then(resolve, reject){
    3. resolve('thenable')
    4. }
    5. }
    6. let myPromise = Promise.resolve(thenable);
    7. console.log(myPromise) // Promise {<fulfilled>: "thenable"}

    3、如果传一个不是thenable的对象,返回一个fullfilled的promise,并将参数传给then的回调函数。
    4、如果不传参数,则返回一个fullfilled的promise

    (2)快速创建一个rejected的promise - Promise.reject
    参数会原封不动地传递给catch回调函数