Promise入门到自定义

458 阅读10分钟

Promise入门到自定义

一. 准备

1.什么是回调函数?

回调只是使用JavaScript函数的惯例的名称。 JavaScript语言中没有特别的东西叫做“回调”,它只是一个约定。不像大多数函数那样立即返回一些结果使用回调函数需要一些时间来产生结果。 “异步”这个词,”,意思是“需要一些时间”或“将来会发生,而不是现在”。通常回调仅在进行I / O时使用,例如下载东西,阅读文件,与数据库交互等

2.回调函数的分类

  • 同步回调
    • 理解: 立即执行, 全部执行完了才结束, 不会放入回调队列中
    • 例子: 数组遍历相关的回调函数 / Promise的 executor 函数
  • 异步回调
    • 理解: 不会立即执行, 会放入回调队列中来执行
    • 例子: 定时器回调 / ajax回调 / Promise的成功|失败的回调
  • 简单来理解就是:同步按你的代码顺序执行,异步不按照代码顺序执行,异步的执行效果更高

3.JS中的error处理

  • 错误的类型

    • Error: 所有错误的父类型

    • ReferenceError: 引用的变量不存在

    • TypeError: 数据类型不正确的错误

    • RangeError: 数据值不在其所允许的范围内

    • SyntaxError: 语法错误

  • 错误处理

    • 捕获错误: try...catch(error){}
    • 抛出错误: throw error
  • error对象属性

    • message属性: 错误的相关信息
    • stack属性: 函数调用栈记录信息



二. Promise理解和使用

1.Promise是什么

Promise 是异步编程的一种解决方案, 解决回调地狱, 主通常用来标识一个异步操作的最终完成 (或失败)

从语法上来说: promise是一个对象, 用它可以获取异步操作的消息

从功能上来说: promise对象用来封装一个异步操作, 并可以获取成功/失败的结果

function asyncFunction() {
  return new Promise((resolve, reject) => { /* executor */
    setTimeout(() => {
      resolve('Async Hello world')
    }, 16);
  })
}

asyncFunction().then(value => {
  console.log(value) // Async Hello world
})
.catch(err => {
  console.log(err)
})

2.Promise的状态

  • new Promise实例化的promise对象有以下三个状态
    • pending(进行中)
      • 等待状态, 比如正在进行网络请求, 或者定时器没有到时间
    • resolved(已成功)又名fulilled
      • 成功状态, 当我们主动回调了resolve 时, 就处于该状态并且会回调.then()
    • rejected(已失败)
      • 失败状态, 当我们主动回调了 reject 时, 就处于该状态并且会回调.catch()
  • promise的状态改变
    • 1.从 pending 变化 resolved
    • 2.从 pending 变化 rejected
    • 说明: 只有可能这两种状态, 且状态状态改变后, 就不会在再发生任何变化

Promise对象特点

  1. 对象的状态不受外界影响, 只有异步操作的结果, 可以决定当前是哪一种状态, 任何其他操作都无法改变这个状态
  2. 一旦状态改变, 就不会再变, 任何时候都可以得到这个结果

3.Promise的优点和缺点

  1. 优点

    • 有了Promise对象, 就可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数
    • Promise对象提供统一的接口, 使控制异步操作更加容易
  2. 缺点

    • 首先, 无法取消Promise, 一旦新建它就会立即执行, 无法中途取消
    • 其次, 如果不设置回调函数, Promise内部会抛出错误, 不会反映到外部
    • 当处于pending状态时, 无法得知目前进展到哪一个阶段 (刚刚开始还是即将完成)

4.Promise API

实例方法

Promise构造函数: Promise (excutor)

  • excute函数: 同步执行器 (resolve, reject) => { }
  • resolve函数: 将异步操作成功的value传递并调用
  • reject函数: 将异步操作失败的reason传递并调用

Promise.prototype.then(onResolved, onRejected)

  • onResolve函数: 成功时的回调函数 (value) => { }
  • onRejected函数: 失败的回调函数 (reason) => {}
  • 说明: 得到成功 value 的回调和得到失败 reason 的回调, 会返回一个新的promise对象

Promise.prototype.catch(onRejected)

  • onRejected函数: 失败的回调函数
  • 说明: .then()的语法糖, 相当于: then(undefined, onRejected)

静态方法/函数对象方法

Promise.resolve(value)

  • value: 成功的数据或promise对象
  • 说明: 返回一个成功/失败的 promise 对象

Promise.reject(reason)

  • reason: 失败的原因
  • 说明: 返回一个状态失败的 Promise 对象, 并将给定的失败信息传递给对应的处理方法

Promise.all([promise,..])

  • 参数: 数组中传递promise对象
    • 数组中promise对象状态
      • 只要数组传递中的promise对象状态全部成功才返回成功状态的promise对象, 并将成功的 values 以数组传递
      • 只要有一个失败了就回返回失败状态的promise对象, 并将数组其中具体失败promise对象的 reason 信息传递
    • 返回值: 返回新的 promise 对象
  • 注意: 返回数据的顺序以 promise 数组顺序一致

Promise.race([promise,..])

  • 参数: 数组中传递promise对象
    • 数组中promise对象状态
      • 以数组传递的 promise 对象, 第一个完成 promise 的结果状态就是最终的结果状态
      • 返回的结果就是数组中Promise对象第一个状态的 value 或 reason
    • 返回值: 返回新的 promise 对象
  • 注意: 以数组中最先返回结果状态的promise对象为最终结果

5.Promise 几个关键问题

1.如何改变promise的状态

  • resolve(value) : 如果当前是 pending 就会变为 resolved
  • reject(reason) : 如果当前是 pending 就会变为 rejected
  • 抛出异常: 如果当前是 pending 就会变为 rejected

2.一个promise指定多个成功/失败回调, 会调用吗?

  • 当 promise 改变对应的状态时, 对应状态的回调都会调用

3.改变promise状态和指定回调函数谁先谁后?

  • 都有可能, 正常情况下是先指定回调函数再改变状态, 但也可以先改变状态再指定回调函数
  • 如何先该状态再指定回调?
    • 在指定器中直接调用 resolve()/reject()
    • 延迟更长时间才调用 then()
  • 什么时候才得到数据?
    • 如果先指定的回调, 那当状态发生改变时, 回调函数就会放入队列中调用, 得到数据
    • 如果改变的状态, 当指定回到函数时, 回调函数就会放入列中中调用, 得到数据

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

  • 简单说明: 由then()里指定的回调函数, 执行结果决定
  • 详细说明:
    • ①如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常
    • ②如果返回的是非promise 的任意值, 新 promise 变为 resolve, value 为返回的值
    • ③如果返回的是另一个新 promise , 此 promise 的结果就会成为新 promise 的结果

5.promise如何串连多个操作任务?

  • promise 的 then() 返回一个新的 promise, 可以看成 then() 的链式调用
  • 通过 then 的链式调用串连多个同步/异步任务

6.promise异常穿透?

  • 当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调
  • 前面任何操作出了异常, 都会传到最后失败的回调中处理
  • reason是以此进行传递的, 你没有指定 reason 回调, 默认是将 reason throw 抛出了

7.如何中断promise链?

  • 当使用 promise 的 then 链式调用时, 在中间中断promise链, 后续的回调函数不会再调用
  • 方法: 在回调函数中返回一个 pending 状态的 promise 对象

6.Promise基本流程



三. 自定义Promise

1.整体结构思路


2.自定义Promise_class版本

/* 
  自定义Promise模块
  class版本
*/
(function (window) {
  const PENDING = 'pending' // 初始化未确认的状态
  const RESOLVED = 'resolved' // 成功状态
  const REJECTED = 'rejected' // 失败状态

  class Promise {
    /**
     * 通常执行异步任务 usual execute async task
     * @param {function} execute 同步执行器函数
     */
    constructor(execute) {
      // 初始化属性
      const self = this // Promise的实例对象
      self.status = PENDING // 状态属性,初始状态为pending
      self.data = undefined // 用来存储结果数据的属性,初始值为undefined
      self.callbacks = [] // 保存then()传递的回调函数

      /**
       * 将promise状态改为成功,指定成功的value
       * @param {any} value 
       */
      function resolve(value) {
        // 如果当前不是pending状态直接结束
        if (self.status !== PENDING) return
        self.status = RESOLVED // 改变为成功状态
        self.data = value // 数据存储
        // 异步调用所有缓存的待执成功的回调函数
        if (self.callbacks.length > 0) {
          setTimeout(() => {
            self.callbacks.forEach(callbacksObj => {
              callbacksObj.onResolved(value)
            })
          })
        }
      }

      /**
       * 将promise状态改为失败,指定失败的reason
       * @param {any} reason 失败的reason
       */
      function reject(reason) {
        // 如果当前不是pending状态直接结束
        if (self.status !== PENDING) return
        self.status = REJECTED
        self.data = reason
        // 异步调用所有缓存的待执失败的回调函数
        if (self.callbacks.length > 0) {
          setTimeout(() => {
            self.callbacks.forEach(callbacksObj => {
              callbacksObj.onRejected(reason)
            })
          })
        }
      }

      // 调用execute来启动异步任务
      try {
        // 如果同步执行器函数抛异常
        execute(resolve, reject)
      } catch (error) {
        // 改变失败状态,并将失败信息传递
        reject(error)
      }
    }

    /* then()主要功能执行传递的回调或保存回调
      用来指定成功/失败的回调函数
        1. 如果当前promise是resolved,异步执行成功的回调函数onResolved
        2. 如果当前promise是rejected,异步执行失败的回调函数onRejected
        3. 如果当前promise是pending,保存回调函数
      返回一个新的promise对象
        它的结果状态由onResolved或onRejected执行的结果决定
        2.1 抛出error ==> 变为rejected,结果值为error
        2.2 返回值不是promise ==> resolved,结果值为返回值
        2.3 返回值是promise ==> 由这个promise决定新的promise的结果(成功/失败)
    */
    then(onResolved, onRejected) {
      const self = this
      //onResolved的默认值
      onResolved = typeof onResolved === 'function' ? onResolved : (value) => value
      // onRejected的默认值,如果没有传递
      onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason } //将reason向下传递
      // 返回新的promise对象,状态由onResolve或onRejected回调来决定
      return new Promise((resolve, reject) => {
        /**
         * 调用指定的回调函数callback
         * 根据callback执行结果来更新then()返回promise的状态
         * @param {function} callback 要调用的回调函数
         */
        function handle(callback) {
          try {
            // 执行成功的异步回调
            const result = callback(self.data)
            if (result instanceof Promise) {
              result.then(
                value => resolve(value),
                reason => reject(reason)
              )

              // result.then(resolve, reject)
            } else {
              resolve(result)
            }
          } catch (error) {
            reject(error)
          }
        }

        // RESOLVED状态
        if (self.status === RESOLVED) {
          // 异步调用onResolved
          setTimeout(() => {
            handle(onResolved)
          })
          // REJECTED状态
        } else if (self.status === REJECTED) {
          setTimeout(() => {
            handle(onRejected)
          })
        }
        /* PENDING状态,保存传递自定义的的回调, 因为直接将then()传递的回调保存起来时, 后面
            resolve或reject调用的时候 返回的promise状态 一直是PENDING状态, 后面链式调用
            没有效果的 */
        else {
          self.callbacks.push({ // 不是直接成功/失败的回调,保存包含了回调函数调用的函数 
            onResolved(value) { // 在后面调用resolve()中执行
              handle(onResolved)
            },
            onRejected(reason) {
              handle(onRejected)
            }
          })
        }
      })


    }

    /**
     * 用来指定失败的回调函数
     * catch是then的语法糖
     * @param {function} onRejected 失败的回调
     */
    catch (onRejected) {
      return this.then(undefined, onRejected)
    }


    /**
     * value可能是一个一般的值,也可能是promise对象
     * @param {any} value 
     */
    static resolve = function (value) {
      return new Promise((resolve, reject) => {
        if (value instanceof Promise) {
          value.then(resolve, reject)
        } else {
          resolve(value)
        }

      })
    }

    /**
     *  用来返回一个指定reason的失败的promise
     * @param {function} reason 
     */
    static reject = function (reason) {
      return new Promise((resolve, reject) => {
        reject(reason)
      })
    }

    /**
     * 返回一个promise对象,对象的状态由传递过来的promise数组来决定,只有全部成功才返回成功状态的promise
     * @param {Array} promises 
     */
    static all = function (promises) {
      let resolveCount = 0 // 已经成功的数量
      let values = new Array(promises.length) // 用来保存成功promise的value值
      return new Promise((resolve, reject) => {
        // 遍历所有promise,取其对应的结果
        promises.forEach((p, index) => {
          p.then(
            value => {
              // 如果判断传递promise数组全部成功? 计数
              resolveCount++
              values[index] = value
              if (resolveCount === promises.length) { // 全部成功了
                resolve(values)
              }
            },
            reason => {
              reject(reason)
            }
          )
        })
      })
    }

    /**
     * 返回一个promise,对象的状态由传递的promise数组来决定,第一个返回状态的promise就是最终结果
     * @param {Array} promises 
     */
    static race = function (promises) {
      return new Promise((resolve, reject) => {
        // 遍历所有promise,取其对应的结果
        promises.forEach(p => {
          // 返回的promise由第一个完成p来决定其结果
          p.then(resolve, reject)
        })
      })
    }

    /**
     * 延迟指定的时间才执行成功(或失败的)的promise
     * @param {function} value 
     * @param {number} time 
     */
    static resolveDelay = function (value, time) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          if (value instanceof Promise) {
            value.then(resolve, reject)
          } else {
            resolve(value)
          }

        }, time);
      })
    }

    /**
     * 延迟指定的时间才执行失败的promise
     * @param {function} value 
     * @param {number} time 
     */
    static rejectDelay = function (reason, time) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          reject(reason)
        }, time);
      })
    }
  }

  window.Promise = Promise
})(window)