Promise每个细节,你都明白了吗?手写的铺垫

953 阅读9分钟

最近学习了下es6新增的Promise,记录下,把promise所涉及到的知识点都总结一遍,为下一篇,手写Promise做铺垫,喜欢的点个赞,谢谢,你们的点赞是我的动力源泉.

Promise是什么?

理解

  1. 抽象表达:Promise是js中运行异步编程的解决方案( 旧的是啥?-->回调形式)
  2. 具体表达:
    1. 从语法上来说:Promise是一个构造函数
    2. 从功能上来说:Promise对象用来封装一个异步操作并可以获得其结果

Promise的状态改变

  1. pending 变为 resolved
  2. pending 变为 rejected
    • 说明:Promise的状态改变只有这2种,且promise对象只能改变一次
    • 无论成还是失败,都会有一个结果数据
    • 成功结果的数据一般为value,失败结果的数据一般称为reason

promise的基本流程

Promise的基本使用

  1. 创建一个新的promise对象
  2. 执行异步操作任务
  3. 如果成功,调用resolve(value)
  4. 如果失败,reject(reason)
const p = new Promise((resolve,reject) => {//执行器函数 同步回调
    setTimeout(() => {
        const time = Date.now();
        //如果当前时间是偶数就代表成功,否则即代表失败
        if(time%2 == 0){
            resolve(`成功了,返回数据time=${time}`)
        }else{
            reject(`失败了,返回数据time=${time}`)
        }
    },1000)
})
p.then(
    value => {//接收得到成功的value数据  onResolved
        console.log(`成功的回调${value}`)
    },
    reason =>{//接收得到失败的reason数据 onRejected
        console.log(`失败的回调${reason}`)
    }
)

为什么使用Promise?

指定回调方式更加灵活

  • 旧的:必须在启动异步任务指定回调函数
  • Promise:启动异步任务=>返回promise对象=>给promise对象绑定回调函数(甚至可以在异步任务结束后指定)

支持链式调用,这个可以解决回调地狱

  • 回调地狱是啥:回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件
  • 回调地狱的缺点:不便于阅读,不便于异常处理

解决方案: Promise链式调用

中间还有 generator/yard 来解决

终极解决方案: async/await

/*对比不同回调方式(伪代码)*/
//成功的回函数
function successCallback(result) {
  console.log('声音文件创建成功' + result)
}
//失败的回函数
function failureCallback(error) {
  console.log('声音文件创建失败' + error)
}

/* 1.1 纯回调函数 */
//启动任务(audioSettings)前必须指定回调函数(callback)
createAudioFileAsync(audioSettings, successCallback, failureCallback)
//这个咱们可以类比于jQuery的Ajax,在创建请求的时候,
//success于error二个回调函数必须在创建的时候指定,
//不然就没办法处理成功与失败了

/* 1.2 promise */
//可在启动任务(audioSettings)后指定回调函数(callback)
const promise = createAudioFileAsync(audioSettings)
setTimeout(() => {
  promise.then(successCallback, failureCallback)
}, 1000)

/* 2.1 回调地狱 */
//回调函数的嵌套
doSomething(function (result) { //第一个函数function就是sucessCallback
  doSomethingElse(result, function (newResult) {
    doThirdThing(newResult, function (finalResult) {
      console.log('Got the final result' + finalResult)
    }, failureCallback)
  }, failureCallback)
}, failureCallback)

/* 2.2 链式调用 解决回调地狱 但还是有回调的*/
doSomething().then(function(result) { //result是doSomething函数成功执行的返回值
  return doSomethingElse(result)      //执行器函数,同步回调
})
.then(function(newResult){  //newResult是doSomethingElse成功执行的返回值
  return doThirdThing(newResult)
})
.then(function(finalResult){
  console.log('Got the final result' + finalResult)
})
.catch(failureCallback) //统一的错误处理 异常传透

/* 2.3 async/await : 回调地狱的终极解决方案 */
//根本上去掉回调函数
async function request() {
  try{
    const result = await doSomething()
    const newResult = await doSomethingElse(result)
    const finalResult = await doThirdThing(newResult)
    console.log('Got the final result' + finalResult)
  } catch (error) {
    failureCallback(error)
  }
}

如何使用Promise

API

  1. Promise 构造函数:Promise(excutor){}
    1. excutor函数:执行器 (resolve,reject)=>{}
    2. resolve函数:内部定义成功时我们调用的函数 value=>{}
    3. reject函数:内部定义失败时我们调用的函数reason=>{}
    • 说明:excutor会在Promise内部立即同步回调,异步操作在执行器中执行
  2. Promise.prototype.then方法:(onResolved,onRejected)=>{}
    1. onResolved函数:成功的回调函数 (value) => {}
    2. onRejected函数:失败的回调函数 (reason) => {}
    • 说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调,返回一个新的Promise对象
  3. Promise.prototype.catch方法:(onRejected)=>{}
    1. onRejected函数:失败的回调函数 (reason) => {}
    • 说明:then()的语法糖,相当于:then(ndefined,onRejected)
  4. Promise.resolve方法:(value)=>{}
    1. value:成功的数据或Promise对象
    • 说明:返回一个成功或者失败的promise对象
  5. Promise.reject方法:(reason)=>{}
    1. reason:失败的原因
    • 说明:返回一个失败的promise对象
  6. Promise.all方法: (promises) => {}
    1. promises: 包含n个promise的数组
    • 说明: 返回一个新的promise, 只有所有的promise都成功才成功,只要有一个失败了就直接失败
  7. Promise.race方法: (promises) => {}
    1. promises: 包含n个promise的数组
    2. 一旦迭代器中的某个promise成功或失败,返回的 promise就会成功或失败(这一点要注意下,平时我们都是回答只要有成功了就返回,其实在有失败的话也会第一时间返回失败的)
    • 说明: 返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态
  8. Promise.allSettled方法:(promises) => {}
    1. 返回一个在所有给定的promise成功或失败,并带有一个对象数组,每个对象表示对应的promise结果
    2. 说明,这个api目前还是草案(Draft),有读者在评论区提到这个API,所以晚上就更新了
new Promise( (resolve, reject) => {
  setTimeout( () => {
    resolve('成功的数据') //resolve就像是一个传递数据的运输机
  }, 1000 )
})
.then( 
  value => {
    console.log('onResolved()1', value)
  }
)
.catch(
  reason => {
    console.log('onRejected()1', reason)
  }
)

//产生一个成功值为1的Promise对象
const p1 = new Promise((resolve, reject) => {
  resolve(1)
})

const p2 = Promise.resolve(2);
const p3 = Promise.reject(3);
p1.then(value => {console.log(value)})
p2.then(value => {console.log(value)})
p3.then(null,reason => {console.log(reason)})
p3.catch(reason => {console.log(reason)})

const pAll = Promise.all([p1,p2,p3]);//会失败因为p3
const pAll = Promise.all([p1,p2]);
pAll.then(
  values => {
    console.log('all onResolved()', values)
  },
  reason => {
    console.log('all onRejected()', reason)
  }
)
const pRace = Promise.race([p1,p2,p3])
pRace.then(
  value => {
    console.log('race onResolved()', value)
  },
  reason => {
    console.log('race onResolved()', reason)
  }
)

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).then(
    results => {
        console.log(results)
    }
);
//下图为console.log结果

promise的几个关键问题

  1. 如何改变promise的状态? 三种方式去改变状态
    1. resolve(value):如果当前是pending就会变为resolved
    2. reject(reason):如果当前是pending就会变为rejected
    3. 抛出异常:如果当前是pending就会变为rejected
    /* 1.error属于promise哪个状态 */
     const p = new Promise((resolve, reject)=>{
       throw new Error('出错了') //属于reject状态
       //throw 3 //抛出异常 promise状态变为rejected失败状态 reason为抛出的3
     })
    
     p.then(
       value => {},
       reason => { console.log('reason', reason) } //reason Error: 出错了
       //reason: 3
     )
    
  2. 一个promise指定多个成功/失败回调函数,都会调用吗?
    1. 当promise改变为对应状态时都会调用
    const p = new Promise((resolve, reject)=>{
       throw new Error('出错了') //属于reject状态
       //throw 3 //抛出异常 promise状态变为rejected失败状态 reason为抛出的3
     })
    
     p.then(
       value => {},
       reason => { console.log('reason', reason) } //reason Error: 出错了
       //reason 3
     )
     p.then(
       value => {},
       reason => { console.log('reason2', reason) }
     )
     //这种情况也要注意
    p.then(
       value => {},
       reason1 => { console.log('reason1', reason1) } reason1 Error: 出错了
     ).then(
       reason2 => { console.log('reason2', reason2) }  //reason2 undefined
     )
    
  3. 改变promise状态和指定回调函数谁先谁后?
    1. 都有可能,正常情况下是先指定回调函数再改变状态,但也可以先改变状态再指定回调
      //正常的:先指定回调函数,后改变状态
      new Promise((resolve, reject)=>{
          setTimeout(()=>{
              resolve(1)//后改变的状态(同时指定数据) 异步执行回调函数
          },1000)
       }),=.then(//先指定回调函数 保存当前指定的回调函数
          value => {},
          reason => {
              console.log('reason',reason)
          }
       )
      
    2. 如何先改变状态再指定回调?
      1. 在执行器中直接调用resolve()/reject()
        //先改变状态,后指定回调函数
        new Promise((resolve, reject)=>{ //先改变状态(同时指定数据)
            resolve(1)
        }).then(  //后指定回调函数,异步执行回调函数
            value => { console.log('value', value) },
            reason => { console.log('reason', reason) }
        )
        console.log('-----')  
        //先输出----, 再输出value 1
        
      2. 延迟更长时间才调用then()
        const p = new Promise((resolve,reject)=>{     setTimeout(()=>{
                resolve(1)
            },1000)
        })
        setTimeout(()=>{
            p.then( 
                value => { console.log('value', value) },
                reason => { console.log('reason', reason) }
            )
        },1200)
        
    3. 什么时候才能得到数据
      1. 如果先指定的回调,那么当状态发生改变时,回调函数就会调用,得到数据
      2. 如果先改变的状态,那么当指定回调时,回调函数就会调用,得到数据
  4. promise.then()返回的新promise的结果状态由什么决定?
    1. 简单表达:由then()指定的回调函数执行的结果决定
    2. 详细的说:
      1. 如果抛出异常,新的rpromise变为rejected,reason为抛出的异常
      2. 如果返回的是非promise的任意值,新的promise变为resolved,value为返回的值
      3. 如果返回的是另一个新的promise,此promise的结果就会成为新promise的结果
    new Promise((resolve, reject)=>{
      resolve(1)
    }).then(
      value => {
        console.log("onResolved1()", value) // 1
        //return 2  //新Promise状态为resolved, return得到value值
        //return Promise.resolve(3) //返回一个新promise, Promise是函数对象
        //return Promise.reject(5) //返回一个新promise, Promise是函数对象
        throw 4   //新Promise状态为rejected, throw得到value值
      }
    ).then(
      value => {console.log("onResolved2()", value)},
      reason => {console.log("onRejected2()", reason)}
    )
    
  5. promise如何串联多个操作任务
    1. promise的then()返回一个新的promise,可以形成then()的链式调用
    2. 通过then的链式调用串联多个同步/异步任务
    new Promise((resolve, reject) => {
      setTimeout(() => {
        console.log('执行任务1(异步)')
        resolve(1)
      }, 1000)
    }).then(
      value => {
        console.log('任务1的结果', value)
        console.log('执行任务2(同步)')
        return 2
      }
    ).then(
      value => {
        console.log('任务2的结果', value)
    
        return new Promise((resolve, reject) => {
          //启动任务3(异步)
          setTimeout(() => {
            console.log('执行任务3(异步)')
            resolve(3)
          }, 1000)
        })
      }
    ).then(
      value => {
        console.log('任务3的结果', value)
      }
    )
    
  6. promise异常传透
    1. 当使用promise的then链式调用时,可以在最后指定失败的回调
    2. 当前任何操作出了异常,都会传到最后失败的回调最后中处理
    new Promise((resolve, reject) => {
      //resolve(1)
      reject(1)
    }).then(
      value => {
        console.log('onResolved1()', value)
        return 2
      },
      //reason => Promise.reject(reason)
      reason => {
        throw reason
      } //默认failureCallback
    ).then(
      value => {
        console.log('onResolved2()', value)
        return 3
      }
    ).then(
      value => {
        console.log('onResolved3()', value)
      }
    ).catch(reason => {
      console.log('onRejected1()', reason)
    })
    
  7. 中断promise链
    1. 当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数
    2. 办法:在回调函数中返回一个pending状态的promise对象
    new Promise((resolve, reject) => {
      reject(1)
    }).then(
      value => {
        console.log('onResolved1()', value)
        return 2
      }
    ).then(
      value => {
        console.log('onResolved2()', value)
        return 3
      }
    ).then(
      value => {
        console.log('onResolved3()', value)
      }
    ).catch(reason => {
      console.log('onRejected1()', reason)
      return new Promise(()=>{})  //返回一个pending的promise 中断promise链
    }).then(
      value => { console.log('onResolved4()', value) },
      reason => { console.log('onRejected4()', reason)}
    )
    

总结

Promise知识点是真的挺多的,尤其细节方面的东西,更需要多多思考,多总结,点点滴滴的知识,点个赞,谢谢了,你们的点赞是我的动力源泉.然后争取尽快把手写Promise的文章搞出来.