灏天阁

等待者模式

· Yin灏
/**
 * 等待者模式
 * 通过对多个异步进程进行监听,来触发未来发生的操作。
 * 用来解决那些不确定先后完成的异步逻辑
 */

// 等待者对象
var Waiter = function () {
  // 注册的等待对象容器
  var dfd = [],
    // 成功回调方法容器,
    doneArr = [],
    // 失败回调方法容器
    failArr = [],
    // 缓存 Array 方法 slice
    slice = Array.prototype.slice,
    // 保存当前等待者对象
    that = this;
  // 监控对象
  var Primise = function () {
    // 监控对象是否解决成功状态
    this.resolved = false;
    // 监控对象是否解决失败状态
    this.rejected = false;
  };
  // 监控对象类原型方法
  Primise.prototype = {
    // 解决成功
    resolve: function () {
      // 设置当前监控对象解决成功
      this.resolved = true;
      // 如果没有监控对象就取消执行
      if (!dfd.length) return;
      // 遍历所有注册的监控对象
      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 Promise();
  };
  // 回调执行方法
  /**
   * 遍历成功或失败回调函数容器,然后依次执行内部的方法。
   * @param {*} arr 数组
   */
  function _exec(arr) {
    var i = 0,
      len = arr.length;
    // 遍历回调数组执行回调
    for (; i < len; i++) {
      try {
        // 执行回调函数
        arr[i] && arr[i]();
      } catch (e) {}
    }
  }
  // 监控异步方法,参数:监控对象
  that.when = function () {
    // 设置监控对象
    dfd = slice.call(arguments);
    // 获取监控对象数组长度
    var i = dfd.length;
    // 向前遍历监控对象,最后一个监控对象的索引为 length - 1
    for (--i; i >= 0; i--) {
      // 如果不存在监控对象,或者监控对象已经解决,或者不是监控对象
      if (
        !dfd[i] ||
        dfd[i].resolved ||
        dfd[i].rejected ||
        !dfd[i] instanceof Primise
      ) {
        // 清理内存 清空当前监控对象
        dfd.splice(i, 1);
      }
    }
    // 返回等待者对象
    return that;
  };
  // 解决成功回调函数添加方法
  that.done = function () {
    // 向成功回调函数容器中添加回调方法
    doneArr = doneArr.concat(slice.call(arguments));
    // 返回等待者对象
    return that;
  };
  // 解决失败回调函数添加方法
  that.fail = function () {
    // 向失败回调函数容器中添加回调方法
    failArr = failArr.concat(slice.call(arguments));
    // 返回等待者对象
    return that;
  };
};

/**
 * 尝试监控异步
 */
var waiter = new Waiter();
// 第一个彩蛋,5秒后停止
var first = (function () {
  // 创建监听对象
  var dtd = waiter.Deferred();
  setTimeout(function () {
    console.log("first finish");
    // 发布解决成功消息
    dtd.resolve();
  }, 5000);
  // 返回监听对象
  return dtd;
})();
// 第二个彩蛋,10秒后停止
var second = (function () {
  // 创建监听对象
  var dtd = waiter.Deferred();
  setTimeout(function () {
    console.log("second finish");
    // 发布解决成功消息
    dtd.resolve();
  }, 10000);
  // 返回监听对象
  return dtd;
})();

/**
 * 最后要用等待者对象监听两个彩蛋的工作状态,并执行相应的成功回调函数与失败回调函数
 */
waiter
  .when(first, second) // 监听两个彩蛋
  .done(
    function () {
      // 添加成功回调函数
      console.log("success");
    },
    function () {
      console.log("success again");
    }
  )
  .fail(function () {
    // 添加失败回调函数
    console.log("fail");
  });
/*
  输出结果:
     first
     second
     success
     success again
*/

/**
 * 我们看到 10 秒后当第二个彩蛋结束后,我们注册的两个成功回调函数成功执行,当然如果第一个执行失败,那么我们就将执行失败回调函数。
 */
var first = (function () {
  var dtd = waiter.Deferred();
  setTimeout(() => {
    console.log("first finish");
    // 发布解决失败消息
    dtd.reject();
  }, 5000);
  return dtd;
})();
/*
  输出结果:
     first
     fail
     second finish
*/

/**
 * 封装异步请求
 * 将原生的 ajax 方法封装成等待者模式,将 resolve 方法放在请求成功回调函数内调用,将 reject 方法放在请求失败回调函数中调用。
 */
// 封装 get 请求
var ajaxGet = function (url, success, fail) {
  var xhr = new XMLHttpRequest();
  // 创建检测对象
  var dtd = waiter.Deferred();
  xhr.onload = function (event) {
    // 请求成功
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
      success && success();
      dtd.resolve();
    } else {
      // 请求失败
      dtd.reject();
      fail && fail();
    }
  };
  xhr.open("get", url, true);
  xhr.open(null);
};

- Book Lists -