Promise由浅入深

Promise是什么?

  1、Promise是ES6提供的进行异步编程的解决方案

  2、Promise是一个构造函数,用来封装一个异步操作,可以获取其成功或失败的值

 

  异步编程旧的操作都是回调函数的方式:(不利于阅读和异常处理)

    1)fs 文件操作

      require('fs').readFile('./index.js', (err, data) => { })

    2)ajax

      $.get('url', (data) => { })

     3)定时器

      setTimeout(() => { }, 1000);

 

读取文件时采用回调的方式和promise的区别:

复制代码
  const util = require('util')
  const fs = require('fs')

  // 1、回调函数的写法
  fs.readFile('./resources/为学.md', (err, data) => {
    if (err) throw err
    console.log(data + '')
  })

  let readFile = util.promisify(fs.readFile) // 将回调函数风格的方法转为promise

  // 2、promise写法
  readFile('./resources/为学.md').then((data) => {
    console.log(data + '')
  })

  // 3、promise封装读取文件的方法
  function getFile(path) {
    return new Promise((resolve, reject) => {
      require('fs').readFile(path, (err, data) => {
        if (err) reject(err)
        resolve(data)
      })
    })
  }

  getFile('./resources/为学.md')
    .then((data) => {
      console.log(data + '')
    })
    .catch((err) => {
      console.log(err)
    })
复制代码

 

使用promise加载图片:

复制代码
      function loadImage(src) {
        const promise = new Promise((res, rej) => {
          const img = document.createElement('img')
          img.src = src
          img.onload = function () {
            res(img)
          }
          img.onerror = function () {
            const error = new Error(`图片加载失败,url为:${src}`)
            rej(error)
          }
        })
        return promise
      }
      const src1 =
        'https://img2020.cnblogs.com/blog/1742906/202105/1742906-20210514231342257-1073217931.jpg'
      const src2 =
        'https://s.yimg.com/cv/apiv2/default/20181030/500x500/celtics_wbg.png'

      loadImage(src1)
        .then((res) => {
          console.log(res)
          return loadImage(src2)
        })
        .then((res) => {
          console.log(res)
        })
        .catch((err) => {
          console.log(err)
        })
复制代码

 

promise的状态:

  它的状态是实例对象中的 PromiseState 属性的值,共有3个:

    pending      未决定的

    resolved/fulfilled  成功

    rejected      失败

  它的状态变化只有两种可能,一是由pending变为resolved,一是由pending变为rejected,并且一个promise对象的状态只能改变一次,不可能由成功变为失败或由失败变为成功

  如何改变promise对象的状态:

    1、调用reslove(),将promise的状态由pending变为resolved

    2、调用reject(),将promise的状态由pending变为rejected

    3、执行throw语句,可以写成throw new Error('出错啦')也可以写成throw ‘出错啦’,将promise的状态由pending变为rejected

 

  promise对象的值:

  它的值是实例对象中的 PromiseResult 属性,保存了promise对象成功/失败的结果,没有执行成功或者失败该属性值为undefined

    PromiseResult只能由resolve()和reject()改变,保存它们传来的值,在实例对象中用then()接收resolve()的值,用catch()接收reject()的值,或者是用then()方法中两个回调函数,第一个接收resolve()的值,第二个接收reject()的值

 

API:

  Promise(executor){}

  执行器函数(executor函数):(resolve,reject)=>{}

    resolve函数:内部定义成功时调用的函数 value=>{}

    reject函数:内部定义失败时调用的函数 err=>{}

  注意:执行器函数会在Promise内部立即同步调用,异步操作在执行器中执行

 

  then()方法

    (onResolved,onRejected)=>{}

    onResolved函数:成功的函数 (value)=>{}

    onRejected函数:失败的函数 (err)=>{}

 

  catch()方法

    (onRejected)=>{}

    onRejected函数:失败的函数 (err)=>{}

 

构造函数的方法:

  Promise.resolve()

复制代码
    /*
      如果传入的参数为非promise类型的对象,则返回的结果为成功的promise对象
      如果传入的参数为promise类型的对象,则返回的结果决定了resolve的结果
    */
    let p = Promise.resolve(111)
    console.log(p)

    let p1 = Promise.resolve(new Promise((resolve, reject) => {
      reject('错误')
    }))
    p1.catch(err => {
      console.error(err)
    })
复制代码

  Promise.reject()

复制代码
    // 不管传入的是什么,都会返回失败的结果
    let p = Promise.reject(123)
    p.catch(err => { console.log(err) })
    console.log(p)
    // 即使传入的是一个成功的promise对象,返回的也依然是失败的结果,这个结果是成功的promise
    let p1 = Promise.reject(new Promise((resolve, reject) => {
      resolve(456)
    }))
    p1.catch(err => { console.log(err) })
    console.log(p1)
复制代码

  Promise.all()

复制代码
    /*
      如果数组中的promise对象都是成功的状态,那么返回的状态也是成功的,返回的结果是这三个参数组成的数组
      如果数组中的promise对象有一个是失败的状态,那么返回的状态就是失败的,返回的结果是第一个失败的promise的结果
返回的顺序和传入的顺序保持一致
*/ let p = new Promise((resolve, reject) => { resolve('成功') }) let p1 = Promise.resolve('ok') let p2 = Promise.resolve('OK!') const result = Promise.all([p, p1, p2]) console.log(result) // PromiseState 的值为 fulfilled PromiseResult 的值为 ['成功', 'ok', 'OK!']
复制代码

  Promise.all()的应用:

    forEach异步调用问题

  Promise.race()

复制代码
    // race是赛跑的意思,返回的promise对象的状态和结果为数组中第一个执行完的promise的状态和结果
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('成功')
      })
    })
    let p1 = Promise.reject('err')
    let p2 = Promise.resolve('OK!')

    let result = Promise.race([p, p1, p2])
    console.log(result) // 返回p1的状态和结果
复制代码

 

Promise的几个问题:

  1、只要promise的状态发生改变,即执行了resolve()或reject()或throw '',它指定的多个成功/失败回调函数都会调用

复制代码
    let p = new Promise((resolve, reject) => {
      // resolve('成功')
      // reject('错误')
      throw '错误'
    })

    p.catch(err => {
      console.log(err)
    })
    p.catch(err => {
      console.log(err)
    })
复制代码

  2、改变promise状态和指定回调函数谁先执行?正常情况下promise中都是异步任务,先执行then()方法再改变promise状态

复制代码
    // 如果promise中是同步任务,那么再改变promise状态再指定回调函数(执行then()方法);如果promise中是异步任务,那么先指定回调函数(先执行then()方法)再改变promise状态,这种情况居多
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('成功')
      }, 100)
    })
    p.then(data => {
      console.log(data) // 此时是先指定回调函数再改变promise状态
    }, err => { console.log(err) })
复制代码

  3、如何先改变promise状态再指定回调函数

    1)在执行器中直接调用 resolve()/reject()----同步任务

    2)延迟更长的时间才调用 then()

  4、什么时候能得到数据

    1)如果先指定回调,那么当状态发生改变时回调函数就会调用,得到数据----异步任务

    2)如果先改变状态,那么当指定回调时回调函数就会调用,得到数据----同步任务

   5、promise.then()返回的新promise的状态和结果由什么决定

    1)then()方法中有两个回调函数,then()返回的promise的状态和结果由原来的promise指定的回调函数执行的结果决定

    2)具体来说:

      ①如果返回的不是promise对象,那么新promise的状态为fulfilled,结果为返回的值。不写return语句,默认是return undefined

      ②如果返回的是promise对象,那么新promise的状态和结果为当前promise的状态和结果

      ③如果抛出异常,新promise的状态为rejected,结果为抛出的值

  6、promise如何串联多个任务:then()方法中return一个promise对象,进行链式调用
复制代码
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('成功')
      }, 100);
    })
    p.then(data => {
      return new Promise((resolve, reject) => {
        resolve('ok')
      })
    }).then(data => {
      console.log(data) // ok
    }).then(data => {
      console.log(data) // undefined 因为上一个then()语句中没有写return,默认是return undefined
    })
复制代码

  7、异常穿透的特性

复制代码
    // 异常穿透:在promise的执行过程中,如果执行了reject()或者throw语句,只需要在最后的catch()中进行处理,中间不需要返回失败的promise
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('成功')
        // reject('失败')
      }, 100);
    })
    p.then(data => {
      return new Promise((resolve, reject) => {
        resolve('ok')
      })
    }).then(data => {
      console.log(data)
      throw '失败啦'
    }).then(data => {
      console.log(data)
      // return new Promise((resolve, reject) => {
      //   reject('失败了')
      // })
    }).catch(err => {
      console.log(err) // 失败啦
    })
复制代码

  8、中断promise链

复制代码
    let p = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('成功')
      }, 100);
    })
    p.then(data => {
      console.log(111)
      // throw 123 // 会执行catch语句
      return new Promise(()=>{}) // 有且只有返回一个pending状态的promise,才会中断promise链
    }).then(data => {
      console.log(222)
    }).then(data => {
      console.log(333)
    }).catch(err => {
      console.log(err)
    })
复制代码

Promise封装

  ES5:

复制代码
    function Promise(executor) {
      this.PromiseState = 'pending' // 实例对象添加状态属性
      this.PromiseResult = null // 实例对象添加结果属性
      this.callbacks = [] // 提供一个数组用于保存所有的回调函数
      const _this = this // 保存实例对象this的值
      // 定义resolve函数
      function resolve(data) {
        if (_this.PromiseState !== 'pending') return // 限制promise的状态只能改变一次
        _this.PromiseState = 'fulfilled' // 修改对象的状态
        _this.PromiseResult = data // 修改对象的结果
        // if(_this.callback.onResolved) _this.callback.onResolved(data) // 为什么在这里调用回调函数而不是在then方法中:异步任务先指定了回调,在状态发生改变时执行回调函数
        _this.callbacks.forEach((item) => {
          item.onResolved(data)
        })
      }
      // 定义reject函数
      function reject(data) {
        if (_this.PromiseState !== 'pending') return // 限制promise的状态只能改变一次
        _this.PromiseState = 'rejected' // 修改对象的状态
        _this.PromiseResult = data // 修改对象的结果
        // if(_this.callback.onRejected) _this.callback.onRejected(data) // 在这里调用回调函数:异步任务先指定了回调,在状态发生改变时执行回调函数
        _this.callbacks.forEach((item) => {
          item.onRejected(data)
        })
      }
      // try{}catch(){} 语句处理throw抛出的异常
      try {
        executor(resolve, reject) // 同步调用执行器函数
      } catch (err) {
        reject(err)
      }
    }

    Promise.prototype.then = function (onResolved, onRejected) {
      // 提供异常穿透。如果then()中的第二个参数没有传递,默认抛出onRejected回调
      if (typeof onRejected !== 'function')
        onRejected = (reason) => {
          throw reason
        }
      // 如果第一个参数没有传递,默认执行onResolved()回调
      if (typeof onResolved !== 'function') onResolved = (data) => data
      return new Promise((resolve, reject) => {
        const callback = (type) => {
          try {
            let result = type(this.PromiseResult)
            if (result instanceof Promise) {
              result.then(
                (data) => {
                  resolve(data)
                },
                (err) => {
                  reject(err)
                }
              )
            } else {
              resolve(result)
            }
          } catch (e) {
            reject(e)
          }
        }
        if (this.PromiseState === 'fulfilled') {
          setTimeout(() => {
            callback(onResolved)
          })
        } else if (this.PromiseState === 'rejected') {
          setTimeout(() => {
            callback(onRejected)
          })
        } else if (this.PromiseState === 'pending') {
          this.callbacks.push({
            onResolved: () => {
              setTimeout(() => {
                callback(onResolved)
              })
            },
            onRejected: () => {
              setTimeout(() => {
                callback(onRejected)
              })
            }
          }) // 保存回调函数
        }
      })
    }

    Promise.prototype.catch = function (onRejected) {
      return this.then(undefined, onRejected)
    }

    Promise.resolve = function (result) {
      return new Promise((resolve, reject) => {
        if (result instanceof Promise) {
          result.then(
            (data) => {
              resolve(data)
            },
            (err) => {
              reject(err)
            }
          )
        } else {
          resolve(result)
        }
      })
    }

    Promise.reject = function (err) {
      return new Promise((resolve, reject) => {
        reject(err)
      })
    }

    Promise.all = function (promiseArr) {
      return new Promise((resolve, reject) => {
        let count = 0,
          list = []
        promiseArr.forEach((item, index) => {
          item.then(
            (data) => {
              count++
              list[index] = data
              if (count === promiseArr.length) resolve(list)
            },
            (err) => {
              reject(err)
            }
          )
        })
      })
    }

    Promise.race = function (promiseArr) {
      return new Promise((resolve, reject) => {
        promiseArr.forEach((item) => {
          item.then(
            (data) => {
              resolve(data)
            },
            (err) => {
              reject(err)
            }
          )
        })
      })
    }
View Code
复制代码

  ES6:

复制代码
    class Promise {
      constructor(executor) {
        this.PromiseState = 'pending' // 实例对象添加状态属性
        this.PromiseResult = null // 实例对象添加结果属性
        this.callbacks = [] // 提供一个数组用于保存所有的回调函数
        const _this = this // 保存实例对象this的值
        // 定义resolve函数
        function resolve(data) {
          if (_this.PromiseState !== 'pending') return // 限制promise的状态只能改变一次
          _this.PromiseState = 'fulfilled' // 修改对象的状态
          _this.PromiseResult = data // 修改对象的结果
          // if(_this.callback.onResolved) _this.callback.onResolved(data) // 为什么在这里调用回调函数而不是在then方法中:异步任务先指定了回调,在状态发生改变时执行回调函数
          _this.callbacks.forEach((item) => {
            item.onResolved(data)
          })
        }
        // 定义reject函数
        function reject(data) {
          if (_this.PromiseState !== 'pending') return // 限制promise的状态只能改变一次
          _this.PromiseState = 'rejected' // 修改对象的状态
          _this.PromiseResult = data // 修改对象的结果
          // if(_this.callback.onRejected) _this.callback.onRejected(data) // 在这里调用回调函数:异步任务先指定了回调,在状态发生改变时执行回调函数
          _this.callbacks.forEach((item) => {
            item.onRejected(data)
          })
        }
        // try{}catch(){} 语句处理throw抛出的异常
        try {
          executor(resolve, reject) // 同步调用执行器函数
        } catch (err) {
          reject(err)
        }
      }
      then(onResolved, onRejected) {
        // 提供异常穿透。如果then()中的第二个参数没有传递,默认抛出onRejected回调
        if (typeof onRejected !== 'function')
          onRejected = (reason) => {
            throw reason
          }
        // 如果第一个参数没有传递,默认执行onResolved()回调
        if (typeof onResolved !== 'function') onResolved = (data) => data
        return new Promise((resolve, reject) => {
          const callback = (type) => {
            try {
              let result = type(this.PromiseResult)
              if (result instanceof Promise) {
                result.then(
                  (data) => {
                    resolve(data)
                  },
                  (err) => {
                    reject(err)
                  }
                )
              } else {
                resolve(result)
              }
            } catch (e) {
              reject(e)
            }
          }
          if (this.PromiseState === 'fulfilled') {
            setTimeout(() => {
              callback(onResolved)
            })
          } else if (this.PromiseState === 'rejected') {
            setTimeout(() => {
              callback(onRejected)
            })
          } else if (this.PromiseState === 'pending') {
            this.callbacks.push({
              onResolved: () => {
                setTimeout(() => {
                  callback(onResolved)
                })
              },
              onRejected: () => {
                setTimeout(() => {
                  callback(onRejected)
                })
              }
            }) // 保存回调函数
          }
        })
      }
      catch(onRejected) {
        return this.then(undefined, onRejected)
      }
      static resolve(result) {
        return new Promise((resolve, reject) => {
          if (result instanceof Promise) {
            result.then(
              (data) => {
                resolve(data)
              },
              (err) => {
                reject(err)
              }
            )
          } else {
            resolve(result)
          }
        })
      }
      static reject(err) {
        return new Promise((resolve, reject) => {
          reject(err)
        })
      }
      static all(promiseArr) {
        return new Promise((resolve, reject) => {
          let count = 0,
            list = []
          promiseArr.forEach((item, index) => {
            item.then(
              (data) => {
                count++
                list[index] = data
                if (count === promiseArr.length) resolve(list)
              },
              (err) => {
                reject(err)
              }
            )
          })
        })
      }
      static race(promiseArr) {
        return new Promise((resolve, reject) => {
          promiseArr.forEach((item) => {
            item.then(
              (data) => {
                resolve(data)
              },
              (err) => {
                reject(err)
              }
            )
          })
        })
      }
    }
View Code
复制代码

async和await:

  async函数:函数的返回值为promise对象,promise对象的结果由async函数执行的返回值决定,和then()中的返回规则一样,具体如下:

    1、如果返回的不是promise对象,那么新promise的状态为fulfilled,结果为返回的值。不写return语句,默认是return undefined
    2、如果返回的是promise对象,那么新promise的状态和结果为当前promise的状态和结果
    3、如果抛出异常,新promise的状态为rejected,结果为抛出的值
  await表达式:await的右侧一般是promise对象,也可以是其他值。如果表达式是promise对象,用try…catch捕获异常时,await返回的是promise成功的值;如果表达式是其他值,直接将此值作为await的返回值
  注意:
    1、await必须要写在async函数中,但async函数中可以没有await
    2、如果await的promise失败了,就会抛出异常,需要通过try{}catch(e){}处理
 
复制代码
      async function fn() {
        let p = new Promise((resolve, reject) => {
          // resolve('ok')
          reject('err')
        })
        try {
          let res = await p
          console.log(res)
        } catch (e) {
          console.log(e)
        }
      }
      fn()
复制代码

  async和await结合读取文件(异步代码写的和同步代码一样)和原来的回调函数方式的区别:

复制代码
    const fs = require('fs')
    const util = require('util')
    const myReadFile = util.promisify(fs.readFile) // 将回调函数风格的方法转为promise

    // 回调函数的方式
    fs.readFile('./resources/为学.html', (err, data) => {
      if (err) throw err
      fs.readFile('./resources/插秧诗.html', (err, data1) => {
        if (err) throw err
        fs.readFile('./resources/观书有感.html', (err, data2) => {
          if (err) throw err
          console.log(data + data1 + data2)
        })
      })
    })

    // async和await的方式
    async function getFile() {
      try {
        let res = await myReadFile('./resources/为学.html')
        let res1 = await myReadFile('./resources/插秧诗.html')
        let res2 = await myReadFile('./resources/观书有感.html')
        console.log(res + res1 + res2)
      } catch (e) {
        console.log(e)
      }
    }
    getFile()
复制代码

  async和await在实际项目中应用:

 

微任务与宏任务:

  宏任务:setTimeout、setInterval、DPM事件、ajax

  微任务:Promise、async/await

 

  执行顺序:微任务 > DOM > 宏任务

复制代码
      const con = $('<p>一段内容</p>')
      $('#box').append(con)
      
      console.log(1)

      setTimeout(() => {
        console.log(2)
        alert('setTimeout')
      }, 0);

      Promise.resolve().then(()=>{
        console.log(3)
        alert('Promise')
      })
复制代码

  上面代码的dom渲染将在then执行完、定时器开启前渲染

 

 

 

 

 

 

 

 

 

 

 

 

 

 

x

posted @   吴小明-  阅读(269)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结
历史上的今天:
2019-12-08 事件代理/事件委托----点击li弹出对应的下标和内容
2019-12-08 jQuery在线引用地址
2019-12-08 获取地址栏参数并转化为对象
点击右上角即可分享
微信分享提示