zoukankan      html  css  js  c++  java
  • JS之手写Promise

    手写一个Promise

    准备

    1.回调函数的分类

    同步回调:
    理解: 立即执行, 完全执行完了才结束, 不会放入回调队列中
    例子: 数组遍历相关的回调函数 / Promise的excutor函数

    const arr = [1, 3, 5]
        arr.forEach(item => { // 遍历回调, 同步回调函数, 不会放入列队, 一上来就要执行完
          console.log(item)
        })
        console.log('forEach()之后')
    

    异步回调

    理解: 不会立即执行, 会放入回调队列中将来执行

    例子: 定时器回调 / ajax回调 / Promise的成功|失败的回调

    2 JS中的Error

    1. 错误的类型
      Error: 所有错误的父类型
      ReferenceError: 引用的变量不存在
      TypeError: 数据类型不正确的错误
      RangeError: 数据值不在其所允许的范围内
      SyntaxError: 语法错误
    2. 错误处理
      捕获错误: try ... catch
      抛出错误: throw error
    3. 错误对象
      message属性: 错误相关信息
      stack属性: 函数调用栈记录信息

    Promise

    1. Promise是什么?

    1. 抽象表达:

      Promise是 JS中进行异步编程的新的解决方案

    2. 具体表达:

      从语法上来说: Promise是一个构造函数

      从功能上来说:Promise对象用来封装一个异步操作并可以获取其结果

    3. promise的状态改变(只有2种,只能改变一次)

      pending 变为 resovled

      pending 变为 rejected

    promise基本流程

    2. 如何使用Promise

    1. 主要API
      Promise构造函数: Promise (excutor) {}
      Promise.prototype.then方法: (onResolved, onRejected) => {}
      Promise.prototype.catch方法: (onRejected) => {}
      Promise.resolve方法: (value) => {}
      Promise.reject方法: (reason) => {}
      Promise.all方法: (promises) => {}
      Promise.race方法: (promises) => {}
    2. 几个重要问题
      • 如何改变promise的状态?
        在执行器中调用 resolve(),或者reject(),或者执行器抛出异常

      • 一个promise指定多个成功/失败回调函数, 都会调用吗?
        都会

      • promise.then()返回的新promise的结果状态由什么决定?
        由回调函数的返回结果决定,具体又分为三种情况。

      • 改变promise状态和指定回调函数谁先谁后?
        无所谓,谁在前面都可以,十分灵活!

      • promise如何串连多个操作任务?
        通过 .then的链式调用

      • promise异常传(穿)透?
        默认的错误回调函数

      • 中断promise链
        return new Promise(()=>{})

    3. 手写Promise

    1. 准备

      (1)status 记录Promise的状态,初始值为pendding状态就是正在进行。

      (2)data 记录Promise返回的数据。

      (3)callbacks ,这是一个数组,用来记录需要执行的回调函数队列,里面存储了成功和失败的回调。

      (4) Promise的参数为一个函数(构造器),函数里面又有两个函数调用,好了,了解清楚之后我们就可以开始写Promise的结构了。

      向外暴露Promise:

      ;(function (window) {
          function Promise(executor){}
          window.Promise = Promise
      })(window)
      
    2. 构造器部分

      既然构造器中有两个可执行函数,那么我们就要实现这两个函数。其实两个函数都是差不多的结构只是状态不同,执行的回调函数不同。那么实现我们上边说了,需要有三种状态,那么我们就需要进行定义,而且还需要定义执行的回调函数队列,并且需要有一个变量来存储存储返回的数据,那么思路捋清楚了就可以开始了:

      /**
         * @param {Function} executor 执行器
         * @return {Promise} A new instance of Promise
         * when catch error set promise.status to rejected
         */
      function Promise(executor) {
          const self = this
      
          self.status = PENDING
          self.data = null
          self.callbacks = []
      
          function resolve(value) {
            // 状态只能改变一次
            if (self.status !== PENDING) {
              return
            }
      
            self.status = RESOLVED
            self.data = value
            if (self.callbacks.length > 0) {
              setTimeout(() => {
                self.callbacks.forEach((callbacksObj) => {
                  callbacksObj.onResolved(value)
                })
              })
            }
          }
          function reject(reason) {
            if (self.status !== PENDING) {
              return
            }
            self.data = reason
            self.status = REJECTED
            if (self.callbacks.length > 0) {
              setTimeout(() => {
                self.callbacks.forEach((callbacksObj) => {
                  callbacksObj.onRejected(reason)
                })
              })
            }
          }
          try {
            executor(resolve, reject)
          } catch (error) {
            reject(error)
          }
        }
      
    3. then方法实现:

      3.1

      then方法中也有两个回调函数,一个成功的回调,一个失败的回调,并且需要返回一个新的Promise,这就是为什么Promise可以链式调用的原因。写之前先捋一下逻辑,首先需要判断传入的是否为回调函数,如果为回调那么执行回调,如果成功回调为基本类型的值,那么次promise就直接变为成功状态,若为失败那么直接向下抛出异常值。

      then(onResolved, onRejected) {
          onRejected = typeof === 'function' ? onRejected : value => value // 否则拿到value值构造一个函数赋值给成功参数
          onRejected = typeof === 'function' ? onRejected : reason => { throw reason } // 否则变为一个向下抛出错误的异常函数
          const self = this // 老样子保存引用
          return new Promise((resolve, reject) => {
              if(self.status === PENDING) { // pending状态我们将回调推入回调函数队列
                  self.callbacks.push({
                      OnResolved(value){}, // 回调里的操作决定了Promise的状态,所以还需要进行验证
                      onRejected(reason){}
                  })       
              }
          } )
      }
      

      3.2

      then方法第二部分(解决回调函数内容)

      /* 
              返回promise的结果由onResolved/onRejected执行结果决定
              1. 抛出异常, 返回promise的结果为失败, reason为异常
              2. 返回的是promise, 返回promise的结果就是这个结果
              3. 返回的不是promise, 返回promise为成功, value就是返回值
              */
      

      所以我们需要一个 handler函数来帮助我们判断回调函数的处理结果,之后才执行相应处理。

      function handler(callback) {
              /* 
              返回promise的结果由onResolved/onRejected执行结果决定
              1. 抛出异常, 返回promise的结果为失败, reason为异常
              2. 返回的是promise, 返回promise的结果就是这个结果
              3. 返回的不是promise, 返回promise为成功, value就是返回值
              */
              try {
                const result = callback(self.data)
                if (result instanceof Promise) {
                  result.then(resolve, reject)
                } else {
                  resolve(result)
                }
              } catch (error) {
                reject(error)
              }
            }
      

      3.3

      then方法第三部分(完善then)

      Promise.prototype.then = function (onResolved, onRejected) {
          const self = this
          // 给参数赋予默认值,默认的函数
          onResolved =
            typeof onResolved === 'function' ? onResolved : (value) => value
          onRejected =
            typeof onRejected === 'function'
              ? onRejected
              : (reason) => {
                  throw reason
                }
      
          // 返回一个 promise
          return new Promise((resolve, reject) => {
            // 定义 handler函数,处理回调函数(利用闭包)
            function handler(callback) {
              /* 
              返回promise的结果由onResolved/onRejected执行结果决定
              1. 抛出异常, 返回promise的结果为失败, reason为异常
              2. 返回的是promise, 返回promise的结果就是这个结果
              3. 返回的不是promise, 返回promise为成功, value就是返回值
              */
              try {
                const result = callback(self.data)
                if (result instanceof Promise) {
                  result.then(resolve, reject)
                } else {
                  resolve(result)
                }
              } catch (error) {
                reject(error)
              }
            }
      
            if (self.status === RESOLVED) {
              // status 为 resolved 加入异步回调队列
              setTimeout(() => {
                handler(onResolved)
              })
            } else if (self.status === REJECTED) {
              // status 为 rejected 加入异步回调队列
              setTimeout(() => {
                handler(onRejected)
              })
            } else {
              // status 为 pending  装入回调数组
              self.callbacks.push({
                onResolved() {
                  // 改变状态
                  handler(onResolved)
                },
                onRejected() {
                  handler(onRejected)
                },
              })
            }
          })
        }
      
    4. catch方法

      catch方法比较简单就是捕获错误,那么我们就可以直接复用then方法。

      Promise.prototype.catch = function (onRejected) {
          return this.then(null, onRejected)
        }
      
    5. resolve方法

      返回一个 Promise对象,其实是一个语法糖

      Promise.resolve = function (value) {
          return new Promise((resolve, reject) => {
            if (value instanceof Promise) {
              value.then(resolve, reject)
            } else {
              resolve(value)
            }
          })
        }
      
    6. reject方法

      类似:

      Promise.reject = function (reason) {
          return new Promise((resolve, reject) => {
            reject(reason)
          })
        }
      
    7. allrace方法

      all方法接受一个数组,数组中的数据为Promise多个实例,也可以是基本类型的值,当数组中的多个Promise的状态全都成功时,这个返回值才是成功值,,否则失败。需要补充的是all方法的成功返回值的顺序与数组中实例顺序是对应的关系。

      race方法的参数也是一个数组,数组中也是一个个promise实例,但是这个方法返回的值并不是数组,而是哪个promise率先执行完毕,那么此promise的状态值就为这个promise的状态值,听起来可能有点绕哈,下面开始代码的实现:

      /**
         * @param {[Promise]} promises The Array of promise
         * @return {Promise} A new promise
         */
        Promise.all = function (promises) {
          const values = new Array(promises.length) // 保存结果
          let resolvedCount = 0 // 统计resolved的Promise个数
          return new Promise((resolve, reject) => {
            promises.forEach((p, index) => {
              Promise.resolve(p).then(
                (value) => {
                  resolvedCount += 1
                  values[index] = value
                  if (resolvedCount === promises.length) {
                    resolve(values)
                  }
                },
                (reason) => {
                  reject(reason)
                }
              )
            })
          })
        }
        /**
         * @param {[Promise]} promises The Array of promise
         * @return {Promise} A new promise
         */
        Promise.race = function (promises) {
          return new Promise((resolve, reject) => {
            promises.forEach((p) => {
              Promise.resolve(p).then(
                (value) => {
                  resolve(value)
                },
                (reason) => {
                  reject(reason)
                }
              )
            })
          })
        }
      

      完整代码

      项目的完整代码

  • 相关阅读:
    复制延迟排查
    [学习笔记]贪心
    主从复制延时判断以及脚本
    [学习笔记]尺取法
    RESET MASTER 和RESET SLAVE 命令的使用方法 注意事项
    女神(goddess)——组合数学
    主从同步设置的重要参数log_slave_updates
    埃及分数&&The Rotation Game&&骑士精神——IDA*
    多源复制开关复制命令和监控
    万圣节后的早晨&&九数码游戏——双向广搜
  • 原文地址:https://www.cnblogs.com/jiaweixie/p/13179720.html
Copyright © 2011-2022 走看看