周亚博的个人博客

  • 首页
  • 盛夏光年
  • 陪风去荡
  • 布响丸啦
我频繁记录着 因为生活值得
  1. 首页
  2. 盛夏光年
  3. 正文

深入了解Promise的用法及原理

2022-08-19 195点热度 0人点赞 0条评论

一、Promise的基本介绍与使用

1、异步编程
  • fs文件操作
  • 数据库操作
  • ajax
  • 定时器

在promise之前都是使用回调函数,但是存在回调地狱问题

1、promise支持链式调用,可以有效解决回调地狱的问题

2、指定回调函数的方式更加灵活

2、promise的基本使用
const p = new Promise((resolve,reject)=>{
  // 此时状态为pending
  if(成功) {
    resolve()
  }else {
    reject()
  }
})
// 分别接收成功和失败的回调
p.then(()=>{},()=>{})
3、util.promisify方法

传入一个遵循错误有线的回调风格的函数(err,data)=>{},最终返回promise版本

const util = require('util')
const fs = require('fs')
// 返回一个新的函数
let mineReadFile = util.promisify(fs.readFile)
mineReadFile('./file/index.txt').then(value=>{
  console.log(value)
})
4、Promise的状态

该状态存储在Promise实例对象中的属性中【PromiseState】

pending未决定的

resolved/fulfilled成功

rejected失败

5、Promise对象的值

实例对象中的另一个值【PromiseResult】,用于储存成功或者失败的结果

resolve()和reject()两个函数都可以修改这个值

通过.then可以取出该值

6、Promise的工作流程

image-20220819095325579

二、Promise API

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

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

2、Promise.prototype.then方法

(onResolved,onRejected)=>{}

  • onResolved函数:成功的回调函数(value)=>{}
  • onRejected函数:失败的回调函数(reason)=>{}
3、Promise.prototype.catch方法

(onRejected)=>{}

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

4、Promise.resolve方法

(value)=>{}

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

返回一个成功/失败的Promise对象

let p = Promise.resolve('ok')
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: 'ok'

let p2 = Promise.resolve(new Promise((resolve,reject)=>{}))

规律:- 如果传入的是非Promise类型的对象,返回结果为rejected的Promise对象

​ - 如果传入一个Promise对象,则结果状态由参数结果状态决定

5、Promise.reject方法

(reason)=>{}

  • reason:失败的原因

无论传入什么类型的参数都返回一个失败的promise对象

6、Promise.all方法

([promise])=>{}

  • [promise]:一个包含多个Promise对象的数组

结果返回一个新的Promise,数组中所有的Promise成功才返回成功,反之失败

let p1 = Promise.resolve('ok')
let p2 = Promise.resolve('ok')
let p3 = Promise.resolve('ok')
let result = Promise.all([p1, p2, p3])
/** [[PromiseState]]: "fulfilled"
 *   [[PromiseResult]]: Array(3)        - 成功返回结果是三个Promise对象结果的数组
 *                                  - 失败结果直接是"Error"
 */                                  
7、Promise.race方法

([promise])=>{}

结果返回一个新的Promise,结果以数组中第一个返回的Promise结果为准

三、Promise关键问题

1、如何修改Promise的状态
  • resolve(value)将pending改为resolved
  • reject(reason)将pending改为rejected
  • 抛出异常可以将pending改为rejected throw err
2、如果使用then方法给一个Promise对象指定多个回调,是否都会调用
  • 当Promise状态改变后都会调用
let p = Promise.resolve('ok')
// 两个都会执行
p.then((value) => { console.log(value) })       // ok
p.then((value) => { console.log(value) })       // ok
3、改变Promise状态和指定回调函数先后问题

两种情况都有可能:

  • 执行器中直接改变状态或者.then的调用是延时的都可以先改变状态
  • 如果执行器中的代码是异步的,会先指定回调后改变状态(多数)
4、Promise.then()返回的Promsise结果由什么决定
let p = new Promise((resolve,reject)=>{
  resolve('ok')
})
let result = p.then(value=>{},reason=>{})
console.log(result)
// result结果是一个Promise对象,结果由回调函数执行结果决定
5、Promise如何串联多个操作任务
  • 通过.then返回新的Promise,可以进行链式调用
  • 通过链式调用实现了串联
let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('ok1')
  }, 1000)
})
p.then(value => {
  return new Promise((resolve, reject) => {
    resolve('ok2')
  })
}).then(value => {
  console.log(value)    // ok2
})

6、Promise的异常穿透

当使用链式调用的时候,只需要在最后使用.catch指定失败的回调,前边任何操作出现失败都会在最后进行处理

7、中断Promise链

在链式调用中想要中断这个链式调用,不再调用后面的回调函数:

返回一个状态为pending的Promise对象return new Promise(()=>{})

四、Promise自定义封装

1、简单实现一个同步功能
// promise.js
function Promise(executor) {
  const self = this
  // Promise状态
  this.PromiseState = 'pending'
  // Promise的结果
  this.PromiseResult = null
  function resolve(data) {
    // 保证状态只能修改一次
    if (self.PromiseState !== 'pending') return
    // 1、修改对象的状态(PromiseState)
    self.PromiseState = 'fulfilled'
    // 2、设置结果值(PromiseResult)
    self.PromiseResult = data
  }
  function reject(data) {
    if (self.PromiseState !== 'pending') return
    self.PromiseState = 'rejected'
    self.PromiseResult = data
  }
  try {// 添加错误捕获实现throw修改状态
    // 同步调用执行器函数
    executor(resolve, reject)
  } catch (err) {
    reject(err)
  }
}
Promise.prototype.then = function (onResoleved, onRejected) {
  // 调用回调函数
  if (this.PromiseState === 'fulfilled') {
    onResoleved(this.PromiseResult)
  }
  if (this.PromiseState === 'rejected') {
    onRejected(this.PromiseResult)
  }
}
// index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <script src="./promise.js"></script>
  <script>
    let p = new Promise((resolve, reject) => {
      resolve('ok')
      reject('err') // 不执行
      throw 'err'       // 不执行
    }).then(value => {
      console.log(value)    // ok
    }, reason => {
      console.log(reason)
    })
  </script>
</body>

</html>
2、如果执行器函数中存在异步操作,导致状态修改延迟,则无法执行回调

在.then方法中对异步进行处理(对处于pending的状态进行处理)

  • 同步任务回调是在执行then方法调用时执行的
  • 异步任务回调的执行要放在状态修改后执行(resolve和reject中)

如果执行then方法时,状态没有发生修改,就不会执行回调函数,应将回调函数保存

—— 此时声明一个对象保存异步待处理回调函数 this.callback={}

Promise.prototype.then = function (onResoleved, onRejected) {
  ...
  if(this.PromiseState === 'pending'){
    this.callback = { onResoleved, onRejected }
  }
}

—— 当修改状态后判断,then是否已经调用过了,是否需要自动执行回调

function resolve(data) {
  // ... 修改状态操作
  if (self.callback.onResoleved) {
    self.callback.onResoleved(data)
  }
}
function reject(data) {
  // ... 修改状态操作
  if (self.callback.onRejected) {
    self.callback.onRejected(data)
  }
}

—— 运行结果

<body>
  <script src="./promise.js"></script>
  <script>
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('ok')
      }, 1000)
    }).then(value => {
      console.log(value)    // 一秒后ok
    }, reason => {
      console.log(reason)
    })
  </script>
</body>
3、现在的封装还无法实现异步指定多个回调

因为通过callback对象保存回调函数,如果有多个回调函数,后一个会覆盖前一个

改进:通过数组保存回调函数

// 声明一个数组保存异步待处理回调函数
this.callback = []

// 将待保存回调函数对象添加到数组中
Promise.prototype.then = function (onResoleved, onRejected) {
  ...
  if (this.PromiseState === 'pending') {
    // 此时状态不确定,不能调用回调函数,暂时保存回调函数
    this.callback.push({ onResoleved, onRejected })
  }
}

// 状态修改完毕后遍历数组执行回调
  function resolve(data) {
        ...
    setTimeout(() => {
      self.callback.forEach(item => {
        item.onResoleved(data)
      })
    })
  }
  function reject(data) {
        ...
    setTimeout(() => {
      self.callback.forEach(item => {
        item.onRejected(data)
      })
    })
  }
4、处理then方法返回的结果

此时then方法可以调用回调函数,但是自身没有返回值

要实现then的结果返回一个Promise,状态由回调函数的结果决定

let result = p.then(value => {
  return value
})
console.log(result)     // undefined

给then方法添加return new Promise

Promise.prototype.then = function (onResoleved, onRejected) {
  return new Promise((resolve, reject) => {
    // 调用回调函数
    if (this.PromiseState === 'fulfilled') {
      try {
        // 接收回调函数的结果作为then函数的结果(Promise||非Promise)
        let result = onResoleved(this.PromiseResult)
        if (result instanceof Promise) {
          result.then(v => {
            resolve(v)
          }, r => {
            reject(r)
          })
        } else {// 不是Promise对象,状态为rejected
          resolve(result)
        }
      } catch (err) {
        resolve(err)
      }
    }
  })
}
<body>
  <script src="./promise.js"></script>
  <script>
    let p = new Promise((resolve, reject) => {
      resolve('ok')
    })
    let result = p.then(value => {
      return new Promise((resolve, reject) => {
        throw 'err'
        // resolve('success')       throw|reject|resolve都能修改状态
      })
    })
    console.log(result)
  </script>
</body>

rejected状态进行同样的处理

image-20220819140706374

image-20220819141000504

5、异步状态下then返回结果的实现

当修改状态是一个异步的操作,执行then方法会暂时存储回调函数

将会回调函数的存储再次进行改进,在保存的回调函数中修改then返回值的状态

Promise.prototype.then = function (onResoleved, onRejected) {
  const self = this
  return new Promise((resolve, reject) => {
        ...
    if (this.PromiseState === 'pending') {
      // 此时状态不确定,不能调用回调函数,暂时保存回调函数
      this.callback.push({
        onResoleved: function () {
          try {
            // 执行回调函数,并获取返回值
            let result = onResoleved(self.PromiseResult)
            if (result instanceof Promise) {
              result.then(v => {
                resolve(v)
              }, r => {
                reject(r)
              })
            } else {
              resolve(result)
            }
          } catch (err) {
            reject(err)
          }
        },
        onRejected: function () {
          try {
            let result = onRejected(self.PromiseResult)
            if (result instanceof Promise) {
              result.then(v => {
                resolve(v)
              }, r => {
                reject(r)
              })
            } else {
              resolve(result)
            }
          } catch (err) {
            reject(err)
          }
        }
      })
    }
  })
}

执行结果

<body>
  <script src="./promise.js"></script>
  <script>
    let p = new Promise((resolve, reject) => {
      setTimeout(()=>{
        resolve('ok')
      },1000)
    })
    let result = p.then(value => {
      return new Promise((resolve, reject) => {
        throw 'err'     // 这个状态的修改影响到了then的返回值状态
      })
    })
    console.log(result)     // rejected状态
  </script>
</body>

image-20220819143955714

6、代码优化
  • 经过分析可以看出以上try{}catch(err){}中存在大量重复代码,需要进行封装
  • 添加定时器实现回调异步执行
  • 对未传入回调函数的情况进行初始化
Promise.prototype.then = function (onResoleved, onRejected) {
  // 未传入回调,初始化
  if (typeof onRejected !== 'function') {
    onRejected = reason => {
      throw reason
    }
  }
  if (typeof onResoleved !== 'function') {
    onRejected = value => value
  }
  const self = this
  return new Promise((resolve, reject) => {
    // 方法的封装
    function callback(type) {
      try {
        // 接收回调函数的结果作为then函数的结果(Promise||非Promise)
        let result = type(self.PromiseResult)
        if (result instanceof Promise) {
          result.then(v => {
            resolve(v)
          }, r => {
            reject(r)
          })
        } else {// 不是Promise对象,状态为rejected 
          resolve(result)
        }
      } catch (err) {
        resolve(err)
      }
    }
    /*---------------------------------------------------*/
    // 调用回调函数
    if (this.PromiseState === 'fulfilled') {
      setTimeout(() => {
        callback(onResoleved)
      })
    }
    if (this.PromiseState === 'rejected') {
      setTimeout(() => {
        callback(onRejected)
      })
    }
    if (this.PromiseState === 'pending') {
      // 此时状态不确定,不能调用回调函数,暂时保存回调函数
      this.callback.push({
        onResoleved: function () {
          callback(onResoleved)
        },
        onRejected: function () {
          callback(onRejected)
        }
      })
    }
  })
}
7、catch方法的封装
// catch方法,调用then方法的基础上只传入第二个回调
Promise.prototype.catch = function (onRejected) {
  return this.then(undefined, onRejected)
}
8、Promise.resolve的实现
Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then(v => {
        resolve(v)
      }, r => {
        reject(r)
      })
    } else {
      resolve(value)
    }
  })
}
let p = Promise.resolve('ok')
console.log(p)  

传入非Promise,状态为成功,传入Promise,结果以Promise结果为准

image-20220819152327561

9 、Promise.reject的实现
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason)
  })
}

无论传入什么,结果都是一个状态为rejected(失败)的Promise

image-20220819153029601

10、Promise.all的实现
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    if (!(promises instanceof Array)) {
      reject(promises + ' is not an array')
    }
    let count = 0
    let successArr = []
    // 遍历
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(v => {
        count++
        successArr[i] = v
        if (count === promises.length) {
          resolve(successArr)
        }
      }, r => {
        reject(r)
      })
    }
  })
}

运行结果

let p1 = Promise.resolve('1')
let p2 = Promise.resolve('2')
let p3 = Promise.resolve('3')
let p4 = Promise.reject('4')

console.log(Promise.all([p1, p2, p3]))
console.log(Promise.all([p1, p2, p3, p4]))

image-20220819154642458

11、Promise.race方法的实现

返回的Promise的状态由数组中最先返回的Promise状态决定

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    if (!(promises instanceof Array)) {
      reject(promises + ' is not an array')
    }
    // 遍历
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(v => {
        resolve(v)
      }, r => {
        reject(r)
      })
    }
  })
}

五、完整代码

function Promise(executor) {
  const self = this
  // Promise状态
  this.PromiseState = 'pending'
  // Promise的结果
  this.PromiseResult = null
  // 声明一个数组保存异步待处理回调函数
  this.callback = []
  function resolve(data) {
    // 保证状态只能修改一次
    if (self.PromiseState !== 'pending') return
    // 1、修改对象的状态(PromiseState)
    self.PromiseState = 'fulfilled'
    // 2、设置结果值(PromiseResult)
    self.PromiseResult = data
    // 3、判断callback是否存入了回调函数,从而自动执行回调
    setTimeout(() => {
      self.callback.forEach(item => {
        item.onResoleved(data)
      })
    })
  }
  function reject(data) {
    if (self.PromiseState !== 'pending') return
    self.PromiseState = 'rejected'
    self.PromiseResult = data
    setTimeout(() => {
      self.callback.forEach(item => {
        item.onRejected(data)
      })
    })
  }
  try {// 添加错误捕获实现throw修改状态
    // 同步调用执行器函数
    executor(resolve, reject)
  } catch (err) {
    reject(err)
  }
}
// then方法
Promise.prototype.then = function (onResoleved, onRejected) {
  if (typeof onRejected !== 'function') {
    onRejected = reason => {
      throw reason
    }
  }
  if (typeof onResoleved !== 'function') {
    onRejected = value => value
  }
  const self = this
  return new Promise((resolve, reject) => {
    // 方法的封装
    function callback(type) {
      try {
        // 接收回调函数的结果作为then函数的结果(Promise||非Promise)
        let result = type(self.PromiseResult)
        if (result instanceof Promise) {
          result.then(v => {
            resolve(v)
          }, r => {
            reject(r)
          })
        } else {// 不是Promise对象,状态为rejected 
          resolve(result)
        }
      } catch (err) {
        resolve(err)
      }
    }
    /*---------------------------------------------------*/
    // 调用回调函数
    if (this.PromiseState === 'fulfilled') {
      setTimeout(() => {
        callback(onResoleved)
      })
    }
    if (this.PromiseState === 'rejected') {
      setTimeout(() => {
        callback(onRejected)
      })
    }
    if (this.PromiseState === 'pending') {
      // 此时状态不确定,不能调用回调函数,暂时保存回调函数
      this.callback.push({
        onResoleved: function () {
          callback(onResoleved)
        },
        onRejected: function () {
          callback(onRejected)
        }
      })
    }
  })
}
// catch方法
Promise.prototype.catch = function (onRejected) {
  return this.then(undefined, onRejected)
}

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

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

// Promise.all方法
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    if (!(promises instanceof Array)) {
      reject(promises + ' is not an array')
    }
    let count = 0
    let successArr = []
    // 遍历
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(v => {
        count++
        successArr[i] = v
        if (count === promises.length) {
          resolve(successArr)
        }
      }, r => {
        reject(r)
      })
    }
  })
}

// Promise.race方法的实现
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    if (!(promises instanceof Array)) {
      reject(promises + ' is not an array')
    }
    // 遍历
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(v => {
        resolve(v)
      }, r => {
        reject(r)
      })
    }
  })
}
标签: Promise 封装 异步
最后更新:2022-08-19

粥呀Bo

所谓惊喜,就是苦苦等候的兔子来了,后面却跟着狼

点赞
< 上一篇
下一篇 >

文章评论

取消回复
目录
  • 一、Promise的基本介绍与使用
      • 1、异步编程
      • 2、promise的基本使用
      • 3、util.promisify方法
      • 4、Promise的状态
      • 5、Promise对象的值
      • 6、Promise的工作流程
  • 二、Promise API
      • 1、Promise构造函数Promise(executor){}
      • 2、Promise.prototype.then方法
      • 3、Promise.prototype.catch方法
      • 4、Promise.resolve方法
      • 5、Promise.reject方法
      • 6、Promise.all方法
      • 7、Promise.race方法
  • 三、Promise关键问题
      • 1、如何修改Promise的状态
      • 2、如果使用then方法给一个Promise对象指定多个回调,是否都会调用
      • 3、改变Promise状态和指定回调函数先后问题
      • 4、Promise.then()返回的Promsise结果由什么决定
      • 5、Promise如何串联多个操作任务
      • 6、Promise的异常穿透
  • 四、Promise自定义封装
      • 1、简单实现一个同步功能
      • 2、如果执行器函数中存在异步操作,导致状态修改延迟,则无法执行回调
      • 3、现在的封装还无法实现异步指定多个回调
      • 4、处理then方法返回的结果
      • 5、异步状态下then返回结果的实现
      • 6、代码优化
      • 7、catch方法的封装
      • 8、Promise.resolve的实现
      • 9 、Promise.reject的实现
      • 10、Promise.all的实现
      • 11、Promise.race方法的实现
  • 五、完整代码

COPYRIGHT © 2022 zhouyaker.cn. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS

陕ICP备2022009272号