promise源码实现、练习题,面试题

一、 为什么要使用promise

1. 指定回调函数的方式更加灵活:

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

2. 支持链式调用, 可以解决回调地狱问题

    什么是回调地狱? 回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调函数执行的条件
    回调地狱的缺点?  不便于阅读 / 不便于异常处理
    解决方案? promise链式调用

3. Promise中的then中执行 onResolved 函数是微任务,需要放入队列

4. Promise中的then执行,靠之前resolve先改变状态,否则不会放入队列

3跟4很关键,看最后一道面试题,以及练习题

  // 成功的回调函数
      function successCallback(result) {
        console.log("声音文件创建成功: " + result);
      }
      // 失败的回调函数
      function failureCallback(error) {
        console.log("声音文件创建失败: " + error);
      }
    
      /* 1.1 使用纯回调函数 */
      createAudioFileAsync(audioSettings, successCallback, failureCallback)
      
    /* 1.2. 使用Promise */
      const promise = createAudioFileAsync(audioSettings); // 2
      setTimeout(() => {
        promise.then(successCallback, failureCallback);
      }, 3000);
      
      /* 
      2.1. 回调地狱
      */
      doSomething(function(result) {
        doSomethingElse(result, function(newResult) {
          doThirdThing(newResult, function(finalResult) {
            console.log('Got the final result: ' + finalResult)
          }, failureCallback)
        }, failureCallback)
      }, failureCallback)
    
      /* 
      2.2. 使用promise的链式调用解决回调地狱
      */
      doSomething()
      .then(function(result) {
        return doSomethingElse(result)
      })
      .then(function(newResult) {
        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) {}
    excutor函数: 同步执行 (resolve, reject) => {}
    resolve函数: 内部定义成功时我们调用的函数 value => {}
    reject函数: 内部定义失败时我们调用的函数 reason => {}
    说明: excutor会在Promise内部立即同步回调,异步操作在执行器中执行
2. Promise.prototype.then方法: (onResolved, onRejected) => {}
    onResolved函数: 成功的回调函数  (value) => {}
    onRejected函数: 失败的回调函数 (reason) => {}
    说明: 指定用于得到成功value的成功回调和用于得到失败reason的失败回调
          返回一个新的promise对象

3. Promise.prototype.catch方法: (onRejected) => {}
    onRejected函数: 失败的回调函数 (reason) => {}
    说明: then()的语法糖, 相当于: then(undefined, onRejected)

4. Promise.resolve方法: (value) => {}
    value: 成功的数据或promise对象
    说明: 返回一个成功/失败的promise对象

5. Promise.reject方法: (reason) => {}
    reason: 失败的原因
    说明: 返回一个失败的promise对象

6. Promise.all方法: (promises) => {}
    promises: 包含n个promise的数组
    说明: 返回一个新的promise, 只有所有的promise都成功才成功, 只要有一个失败了就直接失败
    
7. Promise.race方法: (promises) => {}
    promises: 包含n个promise的数组
    说明: 返回一个新的promise, 第一个完成的promise的结果状态就是最终的结果状态

三、promise中几个关键性问题?

1、如何改变promise的状态?
  (1)resolve(value): 如果当前是pendding就会变为resolved
  (2)reject(reason): 如果当前是pendding就会变为rejected
  (3)抛出异常: 如果当前是pendding就会变为rejected
2、 一个promise指定多个成功/失败回调函数, 都会调用吗?
  当promise改变为对应状态时都会调用
3、改变promise状态和指定回调函数谁先谁后?
  (1)都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
  (2)如何先改状态再指定回调?
    ①在执行器中直接调用resolve()/reject()
    ②延迟更长时间才调用then()
  (3)什么时候才能得到数据?
    ①如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
    ②如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
4、promise.then()返回的新promise的结果状态由什么决定?
  (1)简单表达: 由then()指定的回调函数执行的结果决定
  (2)详细表达:
      ①如果抛出异常, 新promise变为rejected, reason为抛出的异常
      ②如果返回的是非promise的任意值, 新promise变为resolved, value为返回的值
      ③如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果 
5、promise如何串连多个操作任务?
  (1)promise的then()返回一个新的promise, 可以开成then()的链式调用
  (2)通过then的链式调用串连多个同步/异步任务
6、promise异常传/穿透?
  (1)当使用promise的then链式调用时, 可以在最后指定失败的回调, 
  (2)前面任何操作出了异常, 都会传到最后失败的回调中处理(倒不如说开始都会定义好then/catch函数,等待被执行)
7、中断promise链?
  (1)当使用promise的then链式调用时, 在中间中断, 不再调用后面的回调函数
  (2)办法: 在回调函数中返回一个pendding状态的promise对象

Promise 源码实现

(function (window) {
  /*
    1、promise接收一个回调函数,excutor 
    2、promise中定义两个函数, resove,reject
    3、3 个状态 rejected 、 resolved、 pending
    4、执行 excutor 回调函数
  */
  const REJECTED = 'rejected',
  RESOLVED = 'resolved',
  PENDING = 'pending'
  function Promise(excutor){
    // debugger

    const that = this
    console.log('看看Promise中的this',that)
    that.data = undefined
    that.status = PENDING
    that.callBack = []


    function resolve(value) {
      if (that.status !== PENDING) {
        return
      }
      that.status = RESOLVED
      that.data = value
      if (that.callBack && that.callBack.length > 0) {
        // debugger
        that.callBack.forEach(callsObj => {
          setTimeout(() => { // 模拟promise微任务
            callsObj.onResolvedcallback(value) //这里只需要调用成功的回调函数
          })
        })
      }
    }

    function reject(reson) {
      if (that.status !== PENDING) {
        return
      }
      that.status = REJECTED
      that.data = reson
      if (that.callBack && that.callBack.length > 0) {
        that.callBack.forEach(callsObj => {
          setTimeout(() => { // 模拟promise微任务
            callsObj.onRejectedcallback(reson) //这里只需要调用成功的回调函数
          })
        })
      }
    }

    try {
      excutor(resolve, reject)
    } catch(error) {
      reject(error)
    }
  }

  // 这里判断promise需要注意,如果不是promise,可能会try catch中捕获
  function isPromise(obj) {
    if (obj) {
      console.log('typeof obj.then', typeof obj.then) // 原型中是通过.then获取不到,需要创建实例对象来获取
      return (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
    }
    return false
  }

  /**
    第一种:promise.then 为 promise 的原型对象
    1、返回一个promise 对象
    2、resolve / reject 执行都是微任务
    3、指定 resolve / reject 的回调
    4、返回promise的结果由 onResolved/ onRejected 执行结果决定
   */

    Promise.prototype.then = function(onResolved, onRejected) {
      // 最后完善一下,默认没有传参,如何自定义
      onResolved = typeof onResolved === 'function' ? onResolved : value => value
      onRejected = typeof onRejected==='function' ? onRejected : reason => {throw reason}

      let that = this
      console.log('看看then中的this',that)

      return new Promise((resolve, reject) => {


        function handleCallBack(callBack) {
          try {
            const result = callBack(that.data)
            console.log('94', isPromise(result));
            if (isPromise(result)) {
              result.then(resolve, reject)
            } else {
              resolve(result)
            }
          } catch (error) {
            console.log('100', error);
            reject(error)
          }
        }
        /*
          1、如果成功,调 onResolved,因为执行下一个then时,接收的是上一个promise返回的结果
          2、执行 回调函数,判断返回的值
            2.1 如果是promise,返回promise的结果就是这个结果 (有疑问)
            2.2 如果不是promise,返回promise为成功,value就是返回的值
            2.3 抛出异常,返回promise的结果为失败,reason为异常
        */
        if (that.status === RESOLVED) {
          setTimeout(() => {
            handleCallBack(onResolved)
          })

        } else if (that.status === REJECTED) {
          setTimeout(() => {
            handleCallBack(onRejected)
          })
        } else {
          // 将成功和失败的回调函数保存callbacks容器中缓存起来,等待 excutor 中 延迟执行的 resolve 直接获取结果
          that.callBack.push({
            onRejectedcallback() {
              handleCallBack(onResolved)
            },
            onResolvedcallback() {
              handleCallBack(onResolved)
            }
          })
        }
      })
    }

    /**
     * 第二个: promise的catch函数
     * @description: primise的catch函数
     * @param {Function} onRejected
     * @return {Promise} 返回一个promise
     * 为什么可以用then实现,如果在 then中使用 new Error,可以在handleCallBack函数中catch捕获到错误
     */
    Promise.prototype.catch = function (onRejected) {
      return this.then(undefined, onRejected)
    }



    /**
     * @description: promise.resolve,返回一个成功的promise
     * @return {Promise}
     * 1、返回一个promise
     * 2、返回promise的状态为成功的
     * 3、是promise的函数对象
     */
    Promise.resolve = function(value) {
      return new Promise((resolve, reject) => {
        if (isPromise(value)) {
          value.then(resolve, reject)
        } else {
          resolve(value)
        }
      })
    }
    
    // 用老师这种实现的resolve/reject实现的resolve有问题,返回的类型会多一层promise
    /**
     * @description: promise.reject,返回一个失败的promise
     * @return {Promise}
     * 1、返回一个promise
     * 2、返回promise的状态为失败的的
     * 3、是promise的函数对象
     */
    Promise.reject = function(reason) {
      return new Promise((resolve, reject) => {
        if (isPromise(reason)) {
          reject.catch(reject)
        } else {
          reject(reason)
        }
      })
    }

    /**
     * @description: 
     * 1、会收集所有的promise,并返回一个数组
     * 2、只要有一个返回reject,那么最终的结果就是失败的
     * 3、promise.all中接收数字,同样会返回
     * @param {Array} promises
     * @return {*}
     */    
    Promise.all = function(promises) {
      if (promises && promises.length > 0) {
        const values = new Array(promises.length)
        return new Promise((resolve, reject) => {
          promises.forEach((p, index) => {
            
          })
        })
        
        return
      }
      return []
    }

  window.Promise = Promise
  window.isPromise = isPromise
})(window)

Promise 练习题

    // 1、 promise异常传/穿透? 
    new Promise((resolve,reject) => {
      setTimeout(() => {
        // resolve(1)
        reject('123')
        // throw new Error('出错了') // 抛出异常, promse变为rejected失败状态, reason为 抛出的error
        // throw 3 // 抛出异常, promse变为rejected失败状态, reason为 抛出的3
      })
    }).then(
    ).catch((error) => {
      console.log('error: ', error);
    })
 // 2、Promise的.then中两个函数接收promise函数,怎么执行
    new Promise((resolve,reject) => {
      setTimeout(() => {
        // resolve(1)
        reject('123')
        // throw new Error('出错了') // 抛出异常, promse变为rejected失败状态, reason为 抛出的error
        // throw 3 // 抛出异常, promse变为rejected失败状态, reason为 抛出的3
      })
    }).then(
      value => {console.log('value1', value)},
      reason => { 
        console.log('reason1', reason) 
        const promiseResolve = Promise.resolve('hhhhhhh')
        return  promiseResolve// 返回了Promise的,状态 = resolved, data = ‘hhhhhh’
      }, '第一个this'
    )
    .then(
      value => {
        console.log('value2', value)
        return 'resolve2()执行之后影响谁的结果'
      },
      reason => { 
        console.log('reason2', reason) 
      }, '第二个this'
    )

Promise面试题:

一、
      setTimeout(() => {
        console.log(1)
      }, 0)
      new Promise((resolve) => {
        console.log(2)
        resolve()
      }).then(() => {
        console.log(3)
      }).then(() => {
        console.log(4)
      })
      console.log(5)
二、
   const first = () => (
        new Promise((resolve, reject) => {
          console.log(3)
          let p = new Promise((resolve, reject) => {
            console.log(7)
            setTimeout(() => {
              console.log(5)
              resolve(6)
            }, 0)
            resolve(1)
          })
          resolve(2)
          p.then((arg) => {
            console.log(arg)
          })
      
        })
      )
    
      first().then((arg) => {
        console.log(arg)
      })
      console.log(4)
三、
      setTimeout(() => {
        console.log("0")
      }, 0)
      new Promise((resolve,reject)=>{ // 1promise
        console.log("1")
        resolve() //1
      }).then(()=>{        
        console.log("2")
        new Promise((resolve,reject)=>{ // 2promise
          console.log("3")
          resolve() //2
        }).then(
          ()=>{      console.log("4")},
          ()=>{} 
        ).then(
          ()=>{       console.log("5")},
          ()=>{})
      }).then(
        ()=>{  console.log("6")},
        ()=>{}
      )
      new Promise((resolve,reject)=>{ // 3promise
        console.log("7")
        resolve()
      }).then(()=>{         
        console.log("8")
      })

你可能感兴趣的:(javascript,javascript,前端,vue.js)