跳至主要內容

等待者模式

微信公众号:储凡2023/11/29大约 12 分钟

等待者模式

等待者模式是通过对多个异步任务进行监听,当异步任务完成后触发未来发生的动作,在没有Promise 这个模型的时候,其实就已经出现这样类似的技术方案,不同的只是没有定制为一个技术规范,等待者模式不属于一般定义的23 种设计模式的范畴,而通常将其看作广义上的技巧型设计模式。

基本介绍

等待者模式就是通过对异步进程监听,来触发未来发生的动作,举个例子当异步进程操作A、B需要都完成以后才能进行C 进程操作,在开发中经常会这样,需要等到上个操作完成或者知道上个操作完成才去触发下个操作,而JavaScript 又是单线程的,不能采用阻塞的方式去处理,在Promise 规范出现之前通常都是使用回调的方式实现,这样很容易造成回调地狱,等待者模式就是在Promise规范制定之前一个类似于Promise 的解决方案,可以算是Promise规范的一个参考前身。

实现

function Waiter() {
  let dfd = [] // 等待对象容器
  let doneArr = [] // 成功回调容器
  let failArr = [] // 失败回调容器

  // 监控对象类
  const Promise = function () {
    this.resolved = false // 监控对象是否解决成功状态
    this.rejected = false // 监控对象是否解决失败状态
  }

  Promise.prototype = {
    // 解决成功
    resolve() {
      this.resolved = true // 设置当前监控状态是成功
      if (!dfd.length)
        return void 0
      for (let i = dfd.length - 1; i >= 0; i--) {
        // 对象监控对象遍历如果任一个对象没有解决或者失败就返回
        if (dfd[i] && !dfd[i].resolved || dfd[i].rejected)
          return void 0
        dfd.splice(i, 1)
      }
      _exec(doneArr)
    },
    // 解决失败
    reject() {
      this.rejected = true // 设置当前监控状态是失败
      if (!dfd.length)
        return void 0 // 没有监控对象取消
      dfd.splice(0) // 清除监控对象
      _exec(failArr)
    }
  }

  this.Deferred = function () {
    return new Promise()
  }

  // 回调执行方法
  function _exec(arr) {
    for (let i = 0, len = arr.length; i < len; i++) {
      try {
        arr[i] && arr[i]()
      }
      catch (e) {
        // console.warn("Error", e);
        _exec(failArr)
      }
    }
  };

  // 监控异步方法参数
  this.when = function (...args) {
    // 设置监控对象
    dfd = args
    let i = args.length
    // 向前遍历监控对象
    for (--i; i >= 0; i--) {
      // 不存在监控对象 监控对象已经解决 监控对象失败
      if (!args[i] || args[i].resolved || args[i].rejected || !args[i] instanceof Promise) {
        args.splice(i, 1)
      }
    }
    return this // 返回等待者对象
  }

  // 解决成功回调函数添加方法
  this.done = function (...args) {
    doneArr = doneArr.concat(args) // 向成功毁掉函数容器中添加回调方法
    return this
  }

  // 解决失败回调函数添加方法
  this.fail = function (...args) {
    failArr = failArr.concat(args) // 向失败回调函数中添加方法
    return this
  }
}(function () {
  const waiter = new Waiter() // 创建一个等待者实例
  const first = (function () {
    const promise = waiter.Deferred()
    setTimeout(() => {
      promise.resolve()
    }, 1000)
    return promise // 返回监听这对象
  }())
  const second = (function () { // 第二个对象
    const promise = waiter.Deferred()
    setTimeout(() => {
      promise.resolve()
    }, 2000)
    return promise
  }())
  waiter.when(first, second).done(() => {
    console.log('success')
  }).fail(() => {
    console.log('fail')
  })
})()

;(function () {
  const waiter = new Waiter() // 创建一个等待者实例
  const first = (function () {
    const promise = waiter.Deferred()
    setTimeout(() => {
      promise.resolve()
    }, 1000)
    return promise // 返回监听这对象
  }())
  const second = (function () { // 第二个对象
    const promise = waiter.Deferred()
    setTimeout(() => {
      promise.resolve()
    }, 3000)
    return promise
  }())
  waiter.when(first, second).done(() => {
    throw new Error('test')
  }).fail(() => {
    console.log('fail')
  })
})()

Promise

Promise就是异步操作的一个解决方案,用于表示一个异步操作的最终完成或失败及其结果值,Promise有各种开源实现,在ES6 中被统一规范,由浏览器直接支持。上面我们实现的等待者模式更类似于Promise.all()

示例

这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise 对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise 对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise 返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable 里第一个触发失败的promise对象的错误信息作为它的失败错误信息。Promise.all方法常被用于处理多个promise对象的状态集合。

const p1 = new Promise((resolve, reject) => {
  resolve('success1')
})

const p2 = new Promise((resolve, reject) => {
  resolve('success2')
})

const p3 = new Promise((resolve, reject) => {
  reject('fail')
})

Promise.all([p1, p2]).then((result) => {
  console.log(result) // 成功状态 // ["success1", "success2"]
}).catch((error) => {
  console.log(error)
})

Promise.all([p1, p3, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error) // 失败状态 // fail
})

更新日志

2025/2/24 19:02
查看所有更新日志
  • 16e12-style: eslint code
  • a3cca-refactor: 替换eslint规则,使用antfu/eslint模块 (#138)
  • c0f2d-refactor: 升级vuepress相关版本,优化项目结构 (#137)
  • e6a79-feat: 大幅新增设计模式、Linux命令、ORM框架教程文档