Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4.Deferred异步回调原理分析及实现 #21

Open
Geek-James opened this issue Jul 25, 2019 · 0 comments
Open

4.Deferred异步回调原理分析及实现 #21

Geek-James opened this issue Jul 25, 2019 · 0 comments
Labels
jQuery about jQuery

Comments

@Geek-James
Copy link
Owner


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

本篇代码为 my-jQuery 1.0.4.js

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

一、基本概念

Promise/A+规范

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

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

  • 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操作扩展到了所有操作,动画、定时中也常用。

二、开始剖析 Deferred

1.Deferred 提供的API

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

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

(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⭐️,对作者也是一种鼓励。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
jQuery about jQuery
Projects
None yet
Development

No branches or pull requests

1 participant