等待者模式

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-12-15

等待者模式(Waiter):通过对多个异步进程监听,来触发未来发生的动作。

var Waiter = function () {
  // 注册了的等待对象容器
  var dfd = [];

  var doneArr = []; // 成功回调方法容器

  var failArr = []; // 失败回调方法容器

  var that = this;

  // 监控对象类
  var MyPromise = function() {
    this.resolved = false;
    this.rejected = false;
  }

  MyPromise.prototype = {
    resolve: function() {
      // 设置当期监控对象成功解决
      this.resolved = true;
      // 没有监控对象则取消执行
      if (!dfd.length) {
        return;
      }

      // 在所有监控对象都被解决时(监控的所有异步任务都resolve)才会执行_exec

      for (var i = dfd.length - 1; i >= 0; i--) {
        // 如果有任意一个监控对象没有被解决或解决失败则方法
        if (dfd[i] && !dfd[i].resolved || dfd[i].rejected) {
          return;
        }
        // 清除监控对象
        dfd.splice(i, 1);
      }
      // 执行所有的成功回调
      _exec(doneArr);
    },
    reject: function() {
      this.rejected = true;
      if (!dfd.length) {
        return;
      }
      // 清除所有的监控对象
      dfd.splice(0); 
      // 执行所有的失败回调
      _exec(failArr);
    },
  }

  // 创建监控对象
  that.Deferred = function () {
    return new MyPromise();
  }

  // 回调执行方法
  function _exec(arr) {
    for(var i = 0, len = arr.length; i < len; i++) {
      try {
        arr[i] && arr[i]();
      } catch (error) {
        
      }
    }
  }; 

  // 监控异步方法 参数:监控对象
  that.when = function() {
    dfd = Array.from(arguments);
    for (var i = dfd.length - 1; i >= 0; i--) {
      // 如果不存在监控对象,或者监控对象已解决,或者不是监控对象,就移除
      if (!dfd[i] || dfd[i].resolved || dfd[i].rejected || !dfd[i] instanceof MyPromise) {
        dfd.splice(i, 1)
      }
    }
    return that;
  };

  // 添加成功回调
  that.done = function() {
    doneArr = doneArr.concat(Array.from(arguments));
    return that;
  }

  // 添加失败回调
  that.fail = function() {
    failArr = failArr.concat(Array.from(arguments));
    return that;
  }
} 

var waiter = new Waiter();

var first = (function () {
  // 创建监听对象
  var dtd = waiter.Deferred();
  setTimeout(() => {
    console.log('first finish')
    // 发送解决成功消息
    dtd.resolve();
  }, 1000)

  return dtd;
})();

var second = (function () {
  // 创建监听对象
  var dtd = waiter.Deferred();
  setTimeout(() => {
    console.log('second finish')
    // 发送解决成功消息
    dtd.resolve();
  }, 3000)

  return dtd;
})();

// 用等待者对象监听两个异步任务
waiter
  .when(first, second)
  .done(function() {
    console.log('success')
  }, function() {
    console.log('success again')
  })
  .fail(function() {
    console.log('fail')
  })

等待者模式可以监听多个异步任务执行成功与否,并可执行响应的回调函数处理,在实战中,意在处理耗时比较长的操作,比如 canvas 中遍历并操作一张大图片中每一个像素点、定时器操作、异步请求等。等待者模式为我们提供了一个抽象的非阻塞方法,只是当异步任务没有全部都“resolved”时,将已经“resolved”的异步任务存储暂时不作处理,只有当所有的异步任务都“resolved”(每个异步任务执行resolve时,都会遍历所有被监听的异步任务,是否都被“resolved”了),才执行注册的成功回调或失败回调。

除此之外,轮询实现的机制也有类似等待者模式,只不过是在其自身内部对未来动作进行了监听。

(function getAjaxData() {
  // 保存当前函数
  var fn = arguments.callee;
  setTimeout(function() {
    $.get('./test.php', function() {
      console.log('轮询一次');
      fn(); // 再一次执行轮询
    })
  }, 5000)
})()