4.实现jQuery的$.Deferred延迟对象

$.Deferred功能描述

 以下代码运行三秒后会在控制台输出"执行完毕".

$.Deferred函数运行会生成一个延时对象dtd,该对象本身具有很多属性比如resolve,reject,progress.其中在一个异步函数中,比如下面代码中的setTimeout或者ajax回调函数里,如果执行dtd.resolve(),则会立刻执行done里面的函数回调.dtd.reject()会触发fail里面的函数回调.dtd.progress()会触发notify里面的函数回调.如果想详细了解$.Deferred延时对象的其他用法可参照jquery文档.

 var wait = function(){
        var dtd = $.Deferred(); // 生成Deferred对象
    var task = function(){
      dtd.resolve('执行完毕');
    };
    setTimeout(task,3000);
       return dtd; 
  };

 $.when(wait())
  .done(function(msg){ console.log(msg); })
  .fail(function(){ alert("出错啦!"); });

 

需求分析

下面我们就来实现一下$.Deferred的核心代码.从上面的调用方式看$.Deferred会返回一个延时对象dtd,dtd上面有resolve,reject和progress方法,由此我们可以确定$.Deferred()的返回值是一个带有上面三个方法的对象.紧接着我们可以使用上节课所学到的$.Callback函数来实现此需求.对于resolve,reject和progress这三种函数对应的状态分别创立三个队列,而后面的done,fail的功能就是往这个队列中执行add()(收集函数),等到异步的逻辑调用完成时例如调用dtd.resolve(),那么就对resolve这个状态的队列执行fire操作依次运行此队列的函数,如此变完成了延迟加载函数的需求.

 

代码实现

将前面讲述的jquery源码系列的第一课的extend函数和第三课的$.Callbacks函数代码实现拷贝到下面文件中,新建一个Deferred函数并暴露在$对象下,Deferred函数的代码便是延迟功能的核心部分.

按照前面所提到的在Deferred函数中新建一个dfr空对象,并将它作为函数的返回值返回.另外需要新建一个promise对象,它里面有state和promise两个方法,其中state()方法调用是为了获取当前的状态,也就是对应着函数里形成的闭包变量let state,另外通过遍历arr数组给promise加上三种队列的add方法.而在dfr对象上挂载队列的fire方法,最后将promise上的所有属性和方法挂载到dfr上.

$.when(dfr)返回值就是Deferred函数定义的变量promise,所以它拥有done和fail这些方法来完成函数收集并装载到对应的队列中,dfr和promise这两个对象共同操作在函数内部形成闭包的三个队列,所以dfr一旦执行resolve,reject和progress函数就会将队列上收集到的函数依次执行.

代码如下:

(function (root) {
  /**
   * 生成配置
   */
  function genConfig(params) {
    const array = params.split(/\s+/);
    const object = {};
    array.forEach((column) => {
      object[column] = true;
    });
    return object;
  }

  function callback(params) {
    const options = typeof params === 'string' ? genConfig(params) : {};

    const list = [];

    let i, fired, start_index; //fired用来记录是否已经使用fire函数触发过

    let memory;

    function fireHandler(context, parameter) {
      fired = true;
      memory = options['memory'] && parameter;
      i = start_index ? start_index : 0;
      for (; i < list.length; i++) {
        if (
          list[i].apply(context, parameter) === false &&
          options['topOnFalse']
        ) {
          break;
        }
      }
      start_index = null;
    }

    const result = {
      add: function () {
        const fn_list = Array.prototype.slice.call(arguments);
        fn_list.forEach((fn) => {
          if (toString.call(fn) === '[object Function]') {
            if (!list.includes(fn) || !options['unique']) {
              list.push(fn);
              if (options['memory'] && fired) {
                start_index = list.length - 1;
                fireHandler(result, memory);
              }
            }
          }
        });
      },
      fire: function () {
        if (!options['once'] || !fired) {
          const parameter = Array.prototype.slice.call(arguments);
          fireHandler(result, parameter);
        }
      },
    };

    return result;
  }

  /**
   * 实现延迟对象功能
   */
  function Deferred() {
    const arr = [
      ['resolve', 'done', callback('once memory'), 'resolved'],
      ['reject', 'fail', callback('once memory'), 'rejected'],
      ['progress', 'notify', callback('memory')],
    ];

    let state = "pending";

    const dfr = {};

    const promise = {
      promise(dfr = null) {
        return dfr === null ? promise : extend(dfr, promise);
      },
      state(){
        return state;
      }
    };

    arr.forEach((item) => {

      const stateString = item[3];
      const list = item[2];

      if(stateString){
        list.add(function (){
          state = stateString;
        })
      }

      dfr[item[0]] = function () {
        if (state !== 'pending') {
          return false;
        }
        list.fire.apply(this, Array.prototype.slice.call(arguments));
      };

      promise[item[1]] = function () {
        const array = Array.prototype.slice.call(arguments);
        array.length > 0 ? list.add(array[0]) : null;
        return promise;
      };
    });

    promise.promise(dfr);

    return dfr;
  }

  const extend = function () {
    var target = arguments[0] || {};

    var i = 1,
      isDeep = false,
      isArrayData = false;

    if (typeof arguments[0] === 'boolean') {
      //第一个参数是boolean型
      isDeep = arguments[0] ? true : false;
      target = arguments[1] || {};
      i = 2;
    }

    var arr = Array.prototype.slice.call(arguments, i);

    if (arguments.length == 1) {
      target = this;
      arr = [arguments[0]];
    }

    arr.forEach((obj) => {
      for (let key in obj) {
        if (isDeep) {
          //深拷贝
          let src = obj[key];
          let des = target[key];
          let copy;
          if (
            jQuery.isPlainObject(src) ||
            (isArrayData = jQuery.isArray(src))
          ) {
            //src是数组或者对象类型
            if (isArrayData) {
              copy = jQuery.isArray(des) ? des : [];
            } else {
              copy = jQuery.isPlainObject(des) ? des : {};
            }

            target[key] = jQuery.fn.extend(isDeep, copy, src); //这一句代码是精髓之处
          } else {
            target[key] = obj[key];
          }

          isArrayData = false;
        } else {
          target[key] = obj[key];
        }
      }
    });

    return target;
  };

  const $ = {
    Callbacks: callback,
    Deferred: Deferred,
    when(defferd) {
      return defferd.promise();
    },
  };
  root.$ = $;
})(window);

 

结果验证

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <script src="./lib/deferred.js"></script>
  </head>
  <body>
 
  </body>
  <script>
    var wait = function () {
      var dtd = $.Deferred(); // 生成Deferred对象
      var tasks = function () {
        dtd.resolve('执行完毕');
        dtd.resolve('执行完毕');
        dtd.reject();
      };

      for (i = 0; i < 10; i++) {
        (() => {
          var j = i;
          setTimeout(() => {
            dtd.progress(j * 10);
          }, i * 1000);
        })();
      }
      setTimeout(tasks, 10000);
      return dtd;
    };

      $.when(wait()).done(function (msg) {
        console.log(msg);
      })
      .fail(function () {
        console.log('发生了错误');
      })
      .notify(function (data) {
        console.log(data + '%');
      });
  </script>
</html>

输出结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值