jQuery源码剖析(四) - Deferred异步回调解决方案

jQuery 源码解析代码及更多学习干货: 猛戳GitHub前端

本篇代码为 my-jQuery 1.0.4.jsgit

建议阅读本篇先弄懂上一篇Callbacks 原理分析,由于Deferred异步回调是基于Callbacks。下载源码而后根据文章思路学习,最好本身边思考边多敲几遍。es6

1、基本概念

Promise/A+规范

首先推荐各位阅读一下 Promise/A+规范github

Promise做为一个模型,提供了一个在软件工程中描述的延时概念的解决方案。ajax

  • 1.Promise表示一个异步操做的最终结果
  • 2.与Promise最主要的交互方法是,经过将函数传入他的then方法,从而得到Promise最终的值或Promise最终拒绝的值的缘由。
  • 3.一个Promise必须处于如下几个状态之中:pending,fulfilled,rejected.
  • 4.一个Promise必须提供一个then方法来获取其值或缘由。

Promise和Deferred的关系

Promise是ES6提出的异步编程模式,能够参考阮一峰ES6- Promise编程

Deferred是jQuery提出的回调函数解决方案,主要依赖Callbacks回调,能够参考上一篇Callbacks原理解析设计模式

主要解决的问题是:当一个异步依赖于另外一个异步请求的结果时,或者某个操做须要等另外几个操做都结束后才开始等,更强大的是从ajax操做扩展到了全部操做,动画、定时中也经常使用。数组

2、开始剖析 Deferred

1.Deferred 提供的API

(1) $.Deferred()生成一个deferred对象。promise

(2)deferred.done() 指定操做成功时的回调函数bash

(3) deferred.fail()指定操做失败时的回调函数

(4) .promise()返回一个Promise对象来观察当某种类型的全部行动绑定到结合,排队与否仍是已经完成。

(5) deferred.resolve() 手动改变deferred对象的运行状态为"已完成",从而当即触发done()方法。

(6)deferred.reject() 这个方法与deferred.resolve()正好相反,调用后将deferred对象的运行状态变为"已失败",从而当即触发fail()方法。

(7) $.when()为多个操做指定回调函数。 除了这些方法之外,deferred对象还有二个重要方法,上面的教程中没有涉及到。

(8)deferred.then() 有时为了省事,能够把done()fail()合在一块儿写,这就是then()方法。

(9)deferred.progress()当Deferred(延迟)对象生成时,调用已添加的处理程序。

2. jQuery封装用法

// jQuery Deferred 写法
var dtd = $.Deferred();// 新建一个Deferred对象
var wait = function(dtd){
    var tasks = function(){
        alert("执行完毕");
        dtd.resolve();//改变Deferred对象的执行状态 成功状态
    };
   setTimeout(tasks,2000);
   return dtd;
};
// 延迟对象的状态 决定调用哪一个队列的处理函数
$.when(wait(dtd))
.done(function(){
    alert("成功啦");
}).fail(function(){
    alert("出错了");
})
//  dtd.resolve(); 改变dtd的执行状态致使done马上执行 
//   dtd.reject(); 改变dtd的执行状态致使fail马上执行 
// dtd.resolve();
复制代码

以上代码输出:

执行完毕
成功啦

经过以上代码,咱们反推jQuery的源码是如何实现的。

3.源码的设计思路:

如下仅为核心代码片断,完整代码请点击源码分析下载

(1)首先定义了tuples的数据结构,用来组装存储异步延迟三种不一样状态信息的描述.

/**
 *  tuples     定义一个数组来存储三种不一样状态信息的描述
 *  第一个参数  延时对象的状态
 *  第二个参数  往队列里添加处理函数
 *  第三个参数  建立不一样状态的队列
 *  第四个参数  记录最终状态信息
 * **/
var tuples = [
  ["resolve","done",jQuery.Callbacks("once memory"),"resolved"],
  ["reject","fail",jQuery.Callbacks("once memory"),"rejected"],
  ["notify","progress",jQuery.Callbacks("memory")]]
复制代码

(2)而后定义一个promise用来封装state,then,promise对象

promise = {
    state :function(){
        return state;
    },
    then:function() {
    },
    promise:function(obj) {
        console.log(promise);
        debugger
        return obj !=null ? jQuery.extend(obj,promise):promise;
    }
    },
复制代码


(3)定义一个延迟对象 deferred = {};
(4)遍历封装好的tuples数组队列,把数组里第二个元素也就是映射到Callbacks而且给到list,将数组里第三个元素记录最终状态的信息给到stateString,而后把数组第一个元素即延时对象的状态映射到Callbacks的add方法上,定义辅助方法deferred[resolveWith],deferred[rejectWith],deferred[notifyWith],最后调用Callbacks的fireWith方法实现队列的回调。

// 遍历 tuples
tuples.forEach(function(tuple,i){
var list = tuple[2], // 建立队列  建立三次 self对象的引用 映射 调用Callbacks里面的方法 
    stateString = tuple[3]; // 拿到当前最终信息的描述
// promise[done | fail |progress]  将这三个状态都拿到Callbacks self里面方法的引用 添加处理函数
promise[tuple[1]] = list.add;

// Handle state 成功或者失败
if (stateString) { //添加第一个处理程序
    list.add(function(){
        // state = [resolved | rejected]
        state = stateString;
    });
}
// deferred [resolve | reject | notify ]  延时对象的状态拿到函数的引用
deferred[tuple[0]] = function(){
    deferred[tuple[0] + "With"] (this === deferred ? promise : this, arguments);
    return this;
};
// list.fireWith 执行队列而且传参
// 调用队列中的处理函数 而且给他们传参 绑定执行时的上下文对象
deferred[tuple[0] + "With"] = list.fireWith;
});
复制代码

(5)将deferred返回出去

// Make the deferred a promise
promise.promise(deferred);
return deferred;
复制代码

(6)定义一个when方法

// 执行一个或多个对象的延迟函数的回调函数
when : function(subordinate){
    return subordinate.promise();
}
});
复制代码

至此,大功告成,实现了jQuery的源码剖析,体会了到做者的数据结构队列处理编程思想

Deferred设计图:

其余

jQuery 源码剖析 系列目录地址:猛戳GitHub

jQuery 源码剖析 系列预计写十篇左右,旨在加深对原生JavaScript 部分知识点的理解和深刻,重点讲解 jQuery核心功能函数、选择器、Callback 原理、延时对象原理、事件绑定、jQuery体系结构、委托设计模式、dom操做、动画队列等。 若是有错误或者不严谨的地方,请务必给予指正,十分感谢。若是喜欢或者有所启发,欢迎 star⭐️,对做者也是一种鼓励。

关注公众号回复:学习 领取前端最新最全学习资料,也能够进群和大佬一块儿学习交流

相关文章
相关标签/搜索