Promise 原理

第一章 Promise 的理解
1.1 Promise 是什么
1.1.1 理解
  1. 抽象表达:
    • Promise 是一门新的技术(ES6规范)
    • Promise 是 JS 中进行异步编程的 新解决方案 ,旧解决方案是单纯使用回调函数
  2. 具体表达:
    • 从语法上来说:Promise 是一个构造函数
    • 从功能上来说:Promise 对象用来封装一个异步操作,并可以获取其 成功/失败 的结果值
1.1.2 应用于异步编程
  • fs 文件操作
    require('fs').readFile('./index.html', (err,data)=>{})
    
  • 数据库操作
  • AJAX
      $.get('/server', (data)=>{})
    
  • 定时器
    setTimeout(()=>{}, 2000);
    
1.2 为什么要使用 Promise
1.2.1 指定回调函数的方式更加灵活
  1. 旧的:必须在启动异步任务前指定
  2. promise:启动异步任务 => 返回 promise 对象 => 给 promise 对象绑定回到函数(甚至可以在异步任务结束后指定多个)
1.2.2 支持链式调用,可以解决回调地狱问题
  1. 什么是回调地狱

    回调函数的嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件

    asyncFun1 (opt, (...arg1) => {
        asyncFun2 (opt, (...arg2) => {
        	asyncFun3 (opt, (...arg3) => {
        		// ...
    		})
    	})
    })
    
  2. 回调地狱的缺点

    • 不便于阅读
    • 不便于异常处理
  3. 解决方案

    • promise 链式调用
1.3 Promise 的状态

实例对象中的一个属性 『PromiseState』

  • pending 未决定的
  • resolved / fulfilled 成功
  • rejected 失败
  • 说明:只有这三种状态,且一个 Promise 对象只能改变一次,无论变成成功还是失败,都会有一个结果数据,成功的结果数据一般称为 value,失败的结果数据一般称为 reason
1.4 Promise 对象的值

实例对象中的另一个属性 『PromiseResult』
保存着异步任务『成功/失败』的结果

  • resolve
  • reject
第二章 Promise 运用
2.1 API
  1. Promise 构造函数:Promise (executor) {}

    • executor 函数:执行器 (resolve, reject) => {}
    • resolve 函数:内部定义成功时调用的函数 value => {}
    • reject 函数:内部定义失败时调用的函数 reason => {}

    说明:executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行

    let p = new Promise ((resolve, reject) => {
        console.log(111)  // 这行代码同步执行
        resolve('ok')
    })
    console.log(222)		// 先输出 111 然后 输出 222
    
  2. Promise.prototype.then 方法:(onResolved, onRejected) => {}

    • onResolved 函数:成功的回调函数 (value) => {}
    • onRejected 函数:失败的回调函数 (reason) => {}

    说明:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 promise 对象

  3. Promise.prototype.catch 方法:(onRejected) => {}

    • onRejected 函数:失败的回调函数 (reason) => {}
  4. Promise.resolve 方法:(value) => {}

    • value:成功的数据或 promise 对象

    说明:返回一个成功/失败的 promise 对象

     // 如果传入的参数为 非Promise 类型的对象,则返回的结果为成功的 promise 对象
        const p = Promise.resolve('ok')
    
        p.then(value => {
          console.log(value)
        })
    
        // 如果传入的参数为 Promise 对象,则参数的结果决定了 resolve 的结果
        const p1 = Promise.resolve(
          new Promise((resolve, reject) => {
            reject('error')
          })
        )
    
        p1.catch(reason => {
          console.log(reason)
        })
    
  5. Promise.reject 方法:(reason) => {}

    • reason:失败的原因

    说明:返回一个失败的 promise 对象,无论参数是普通的字符串还是一个成功的 promise 对象,其返回结果均是一个失败的 promise 对象

  6. Promise.all 方法:(promises) => {}

    • promises:包含 n 个 promise 的数组

    说明:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败

    let p1 = new Promise((resolve, reject) => {
          resolve('ok')
        })
    
        let p2 = Promise.resolve('nice')
    
        let p3 = Promise.resolve('oh')
    
        let p = Promise.all([p1, p2, p3])
        console.log(p)    // [[PromiseState]]: "fulfilled"   [[PromiseResult]]: Array(3)
    
        let p4 = Promise.reject('err')
        
        let pe = Promise.all([p1, p2, p3, p4])
        console.log(pe) // [[PromiseState]]: "rejected"   [[PromiseResult]]: "err"
    
  7. Promise.race 方法:(promises) => {}

    • promises:包含 n 个 promise 的数组

    说明:返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态

    let p1 = new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve('ok')
          }, 1000)
        })
    
        let p2 = Promise.resolve('nice')
    
        let p3 = Promise.resolve('oh')
    
        let p = Promise.race([p1, p2, p3])
        console.log(p)    // nice
    
2.2 Promise 的问题
  1. 改变 Promise 对象的方法
let p1 = new Promise((resolve, reject) => {
      // 1. resolve 函数
      // resolve('ok')   // pending   => fulfilled (resolved)

      // 2. rejected 函数
      // reject('err')     // pending    => rejected

      // 3. 抛出错误
      throw 'error'
    })
    console.log(p1)
  1. 一个 promise 指定多个 成功/失败 回调函数,都会调用吗?

    • 当 promise 改变为对应状态时都会调用
  2. 改变 promise 状态和指定回调函数谁先谁后?

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

    补充:微任务和宏任务,微任务先执行,宏任务后执行

    1. 宏任务(macrotask):
      • 异步 Ajax 请求、
      • setTimeout、setInterval、
      • 文件操作
    2. 微任务(microtask):
      • Promise.then、.catch 和 .finally
      • process.nextTick
      • 其它微任务

    在这里插入图片描述

    在这里插入图片描述

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

    • 简单表达:由 then() 指定的回调函数执行的结果决定
    • 详细表达:
      1. 如果输出异常,新 promise 变为 rejected,reason 为抛出的异常
      2. 如果返回的是非 promise 的任意值,新 promise 变为 resolved,value 为返回的值
      3. 如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果
  4. promise 如何串联多个操作任务?

    • promise 的 then() 返回一个新的 promise,可以变成 then() 的链式调用

    • 通过 then 的链式调用串联多个同步/异步任务

    • let p = new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve(200)
            }, 1000)
          })
      
          let result = p.then(value => {
            console.log(value)      // 200
            return new Promise((resolve, reject) => {
              resolve('success')
            })
          }).then(value => {
            console.log(value)    // success
          }).then(value => {
            console.log(value)    // undefined
          })
      
          console.log(result)
      
  5. promise 异常穿透?

    • 当使用 promise 的 then 链式调用时,可以在最后指定失败的回调

    • 前面任何操作出了异常,都会传到最后失败的回调中处理

    • let p = new Promise((resolve, reject) => {
            setTimeout(() => {
              // reject('出错了')
              resolve('200')
            }, 1000)
          })
      
          p.then(value => {
            console.log(value)      // 200
            return new Promise((resolve, reject) => {
              reject('出错了')
            })
          }).then(value => {
            console.log(value)
          }).then(value => {
            console.log(value)
          }).catch(err => {
            console.warn(err)		// '出错了'
          })
      
  6. 中断 promise 链?

    • 当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的回调函数

    • 办法:在回调函数中返回一个 pending 状态的 promise 对象

    • let p = new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve('200')
            }, 1000)
          })
      
          p.then(value => {
            console.log(value)      // 200
            return new Promise(() => {})      // 下面的then都不再执行
          }).then(value => {
            console.log(value)  
          }).then(value => {
            console.log(value)  
          }).catch(err => {
            console.warn(err)
          })
      
2.3 提前结束promise
<body>
  <button>发起</button>
  <button>取消</button>

  
  <script>
    const obj = {}
    let p
    let button = document.getElementsByTagName('button')[0]
    button.onclick = async function () {
      console.log('5s后自动执行成功')
      p = new Promise((resolve, reject) => {
        obj.reject = reject
        setTimeout(() => {
          resolve(123)
        }, 5000)
      }).then(value => {
        console.log(value)
      }, reason => {
        console.warn(reason)
      })
    }
    
    let cancelButton = document.getElementsByTagName('button')[1]
    cancelButton.onclick = async function () {
      obj.reject('提前执行reject')
    }
  </script>
</body>
第三章 Promise 原理
class Promise {
  // 构造方法
  constructor(executor) {
    // 初始化 PromiseState 和 PromiseResult 属性
    this.PromiseState = 'pending'
    this.PromiseResult = null

    // 保存回调函数
    this.callbacks = []

    // 保存实例对象的 this 的值
    const self = this

    // resolve 函数
    function resolve (data) {
      // 执行以下代码前判断是否已经修改过 promise 的状态了,如果修改过则不能再修改
      if (self.PromiseState !== 'pending') return

      // 1、修改对象的状态(promiseState)
      self.PromiseState = 'fulfilled'

      // 2、修改对象的结果(promiseResult)
      self.PromiseResult = data

      // 调用成功的回调
      setTimeout(() => {
        self.callbacks.forEach(item => {
          item.onResolved(data)
        })
      });
    }

    // reject 函数
    function reject (data) {
      // 执行以下代码前判断是否已经修改过 promise 的状态了,如果修改过则不能再修改
      if (self.PromiseState !== 'pending') return

      // 1、修改对象的状态(promiseState)
      self.PromiseState = 'rejected'

      // 2、修改对象的结果(promiseResult)
      self.PromiseResult = data

      // 调用成功的回调
      setTimeout(() => {
        self.callbacks.forEach(item => {
          item.onRejected(data)
        })
      })
    }

    try {
      // 异步调用【执行器】函数
      setTimeout(() => {
        executor(resolve, reject)
      })
    } catch (error) {
      // 捕获错误,修改 promise 对象状态为【失败】
      reject(error)
    }
  }

  // then 方法
  then (onResolved, onRejected) {
    let self = this

    // 判断回调函数的参数是否存在
    // onRejected 如果没有传递,则为undefined,在实现catch穿透时会出错,所以没有传递时要初始化
    if (typeof onRejected !== 'function') {
      onRejected = reason => {
        throw reason
      }
    }
    // onResolved 如果没有传递,则为undefined,在实现.then值传递时会出错,所以没有传递时要初始化
    if (typeof onResolved !== 'function') {
      onResolved = value => value
    }

    return new Promise((resolve, reject) => {
      // 处理相同功能的函数
      function callFun (funType) {
        try {
          let result = funType(self.PromiseResult)
          if (result instanceof Promise) {
            result.then(v => {
              resolve(v)
            }, r => {
              reject(r)
            })
          } else {
            resolve(result)
          }
        } catch (e) {
          reject(e)
        }
      }

      // 调用回调函数
      if (this.PromiseState === 'fulfilled') {
        setTimeout(() => {
          callFun(onResolved)
        })
      }

      if (this.PromiseState === 'rejected') {
        setTimeout(() => {
          callFun(onRejected)
        })
      }

      // 如果状态为 pending,则说明有可能是个异步任务
      // 避免后面有执行 resolve 或者 reject,将回调保存起来
      if (this.PromiseState === 'pending') {
        this.callbacks.push({
          onResolved: function () {
            callFun(onResolved)
          },
          onRejected: function () {
            callFun(onRejected)
          }
        })
      }
    })
  }

  // catch 方法
  catch (onRejected) {
    return this.then(undefined, onRejected)
  }

  // resolve 方法
  static resolve (value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(v => {
          resolve(v)
        }, r => {
          reject(r)
        })
      } else {
        resolve(value)
      }
    })
  }

  // reject 方法
  static reject (reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }

  // all 方法,全部都为fulfilled才会返回所有结果,其中有rejected则返回reject的结果
  static all (promises) {
    return new Promise((resolve, reject) => {
      // 用于记录成功的条数
      let count = 0

      // 用于保存成功的promise对象
      let arr = []

      for (let i = 0; i < promises.length; i++) {
        if (promises[i] instanceof Promise) {
          promises[i].then(v => {
            // 对象的状态是成功的走这里

            count++
            arr[i] = v
            // 当所有promise对象都成功才可以resolve
            if (count === promises.length) {
              resolve(arr)
            }
          }, r => {
            // 所有对象中有一条失败就直接reject
            reject(r)
          })
        } else {
          count++
          arr[i] = v
          if (count === promises.length) {
            resolve(arr)
          }
        }
      }
    })
  }

  // race 方法 ,哪个执行快就返回哪个,无论是fulfilled还是rejected
  static race (promises) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(v => {
          resolve(v)
        }, r => {
          reject(r)
        })
      }
    })
  }

  // 只要有一个的执行结果是fulfilled,就会返回,如果全为rejected,都结束后reject返回AggregateError: All promises were rejected
  static any (promises) {
    return new Promise((resolve, reject) => {
      let count = 0
      promises.forEach((item, index) => {
        item.then(v => {
          resolve(v)
        }, r => {
          count++
          if (count === promises.length) {
            reject('AggregateError: All promises were rejected')
          }
        })
      })
    })
  }

  // allSettled 方法
  static allSettled (promises) {
    return new Promise((resolve, reject) => {
      let count = 0
      let result = []
      promises.forEach((item, index) => {
        const obj = {}
        count++
        item.then(v => {
          obj.status = 'fulfilled'
          obj.value = v
        }, r => {
          obj.status = 'rejected'
          obj.reason = r
        })
        result[index] = obj
        if (count === promises.length) {
          resolve(result)
        }
      })
    })
  }
}
第四章 async 与 await
4.1 async 函数
  1. 函数的返回值为 promise 对象

  2. promise 对象的结果由 async 函数执行的返回值决定

  3. async function fun () {
          return Promise.reject(321)
     }
     let res = fun()
     console.log(res)
    
4.2 await 表达式
  1. await 右侧的表达式一般为 promise 对象,但也可以是其他的值

  2. 如果表达式是 promise 对象,await 返回的是 promise 成功的值

  3. 如果表达式是其他值,直接将此值作为 await 的返回值

  4. async function fun1 () {
          const p = new Promise((resolve, reject) => {
            resolve(123)
          })
          // 1. 右侧为 promise 的情况
          let res = await p
          console.log(res)    // 123
    
          // 2. 右侧为 非promise 的情况
          let res2 = await 321
          console.log(res2)   // 321
    
          // 3. 右侧为失败的 promise 的情况
          const p2 = Promise.reject('error')
          try{
            let res3 = await p2
          } catch(e) {
            console.warn(e)
          }
        }
        fun1()
    
4.3 注意
  1. await 必须写在 async 函数中,但 async 函数中可以没有 await
  2. 如果 await 的 promise 失败了,就会抛出异常,需要通过 try…catch 捕获处理
第五章 Promise 的演化
5.1 util.promisify
/**
 * util.promisify 方法
 */

// 引入 util 模块
const util = require('util')

// 引入 fs 模块
const fs = require('fs')

// 返回一个 promise 类型的函数
let myReadFile = util.promisify(fs.readFile)

// 使用
myReadFile('./test.md')
  .then(res => {
    console.log(res.toString())
  })
  .catch(err => {
    console.log(err)
  })

  • 15
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值