Deferred在jQuery和Angular中的使用与简单实现
Deferred在jQuery和Angular中的使用与简单实现
Deferred是在jQuery1.5版本中加入的,并且jQuery使用它完全重写了AJax,以前也只是偶尔使用.但是上次在使用Angular做一个小应用的时候,遇到一个问题,
我将我的AJax请求放在了自己定义的factory中,并在factory编写回调函数,返回处理后的对象,然后将这个factory注入到controller中,然后我在controller中就开始对这个对象进行操作,进行一些和View上的数据绑定.
当我这样使用的时候,总是出现了问题.我在controller首次执行的时候,总是拿不到数据,而导致我的界面无法渲染,必须在一定的延迟后再次从服务中获取对象,而由于网络的不稳定性,我不知道需要设置延迟为多少.
这让我纠结很久.
我尝试使用同步aJax,但是Angular中没有提供相应的实现.
想了很久,使用了下面的代码:
factory:
ngApp.factory('UserInfoFactory', ['$http', '$q', function ($http, $q) {
var json = null;
var http = $http({method: 'GET', url: 'XXXX.ashx '});
return {
call:function(fn){
http.success(function(data){
fn(data);
})
}
}
controller
ngApp.controller('MainController', ['$scope', 'UserInfoFactory', function ($scope, UserInfoFactory) { // 引用我们定义的UserInfo服务
var func = function(data){
...
}
UserInfoFactory().call(func);
}]);
以上的代码可以办到,就在整合代码时,想到了一种更好的方式:
deferred
Angular中的deferred服务通过注入$q来实现,下面我们看一个简单的Demo,用来指出我正在干什么.
这是factory的代码
// $q 是内置服务,所以可以直接使用
ngApp.factory('UserInfoFactory', ['$http', '$q', function ($http, $q) {
return {
query : function() {
var deferred = $q.defer(); //申明deferred对象
$http({method: 'GET', url: 'XXXX.ashx '}).
success(function(data) {
deferred.resolve(data); // 声明执行成功,即http请求数据成功,可以返回数据了
}).
return deferred.promise; // 返回承诺,这里并不是最终数据,而是访问最终数据的API
}
};
}]);
其实我不是很懂为什么要用$q来命名,$defer不是更好嘛..
这是controller中的代码
ngApp.controller('MainController', ['$scope', 'UserInfoFactory', function ($scope, UserInfoFactory) { // 引用我们定义的UserInfo服务
var promise = UserInfoFactory.query(); // 同步调用,获得承诺接口
promise.then(function(data) { // 调用承诺API获取数据 .resolve
$scope.user = data;
});
}]);
我在controller里面使用Query拿到了promise对象,然后在then里面执行回调,在这里我已经可以确保我拿到正确调用的回调函数了,
这简直棒极了.
jQuery 中的deferred介绍
因为上面的原因,我打算重新把deferred理一遍,然后自己实现一个简单的deferred.
首先我们来看jQuery中的deferred对象.
使用过jQuery中的deferred的肯定了解,deferred中的三个函数
- 调用三个触发函数resolve,reject,notify,分别会触发done,fail,progres回调函数.
- 三个回调函数, 在里面编写自己的逻辑代码.
- 返回promise对象,只包含三个回调函数.
下面查看一些具体的实例,来了解如何使用deferred.
resolve/done
function cb() {
alert('success')
}
var deferred = $.Deferred()
deferred.done(cb); //在这里会看到3s后才弹出
setTimeout(function() {
deferred.resolve()
}, 3000)
reject/fail
function cb() {
alert('fail')
}
var deferred = $.Deferred()
deferred.fail(cb); // 在这里,3s后弹出fail,表示失败
setTimeout(function() {
deferred.reject()
}, 3000)
notify/progress
function cb(num) {
alert(num)
}
var deferred = $.Deferred()
deferred.progress(cb);// 会不断地弹出数字并递增
var num = 0;
setInterval(function() {
num++;
deferred.notify(num)
}, 2000)
由于notify的这个好处,用来做任务进度非常合适.
我们称 resolve,reject,notify 这3个函数为改变状态的函数,当状态改变,触发回调函数.
由于他们3个函数在deferred对象中,因此可以随意地改变状态以触发回调函数,这太不好了.
因此,deferred还提供了一个对象,promise,它只包含响应的回调函数,而不能改变状态,这常常是很多异步操作暴露出来的接口,比如,jQuery实现的aJax,在封装的过程中改变状态,而仅仅暴露了promise对象.
这也就是promise a+ 规范,是属于CommonJs范畴的,感兴趣的可以多了解.
看下面的函数调用
function getPromise(){
return $.Deferred().promise();
}
try{
getPromise().resolve("a");
} catch(err) {
console.log(err);
}
上述的调用就会抛出异常,promise对象是不能改变状态的.
在deferred和promise中,还有很多其他的函数和属性,then,when,always,state,等待,而且when和then的实现相当复杂和巧妙,但是在这里就不再概述.
下面我们开始实现deferred,
实现deferred
我通过构造函数的方式来创建deferred对象
var deferred = function () {
this._init_();
}
deferred.prototype = {
constructor: deferred,
_init_: function () {
this.resolve_list = [];
this.reject_list = [];
this.notify_list = [];
},
}
这是面向对象封装的一种比较好的方式,也是很多大牛推荐使用的.
恩 比较绕的地方就是new的执行原理,我在JavaScript面向对象高级会介绍.
好了,下面开始主要的逻辑代码
为了避免代码比较绕,我没有使用数组,而是一个一个写出来,这样代码会显得很冗余
deferred.prototype = {
constructor: deferred,
_init_: function () {
this.resolve_list = [];
this.reject_list = [];
this.notify_list = [];
},
resolve: function () {
for (var i = 0; i < this.resolve_list.length; i++) {
this.resolve_list[i].apply(this, arguments);
}
// 在jQuery中,使用了函数管理 $.callbacks('once')的方式,确保resolve只能被调用一次,
// 为了避免引入复杂的逻辑,我就直接把数组清空,这样,resolve也就只能执行一次.
this.resolve_list[i] = [];
// return this 是为了链式编程,我这里返回的还是deferred对象.
return this;
},
done: function (func) {
// 由于 func 可以是多个函数的数组,所以在这里进行一个判断,如果不是数组,那么就变成数组.
if (typeof func === 'function') {
func = [func];
}
this.resolve_list.push.apply(this.resolve_list, func);
return this;
},
reject: function () {
for (var i = 0; i < this.reject_list.length; i++) {
// arguments 参数表示 调用reject传入的参数,可以通过arguments的方式以一个数组的方式获取
// 所以下面的调用也要使用apply.
this.reject_list[i].apply(this, arguments);
}
this.reject_list = [];
return this;
},
fail: function (func) {
if (typeof func === 'function') {
func = [func];
}
this.reject_list.push.apply(this.reject_list, func);
return this;
},
notify: function () {
for (var i = 0; i < this.notify_list.length; i++) {
this.notify_list[i].apply(this, arguments);
}
return this;
},
progress: function (func) {
if (typeof func === 'function') {
func = [func];
}
this.notify_list.push.apply(this.notify_list, func);
return this;
}
}
好了,基本架子已经完成,下面我们测试一下.
var d1 = new deferred();
// 为函数列表添加事件,链式编程.
d1.done(function (arg) {
console.log(arg);
}).fail(function (arg) {
console.log(arg);
}).progress(function (arg) {
console.log(arg);
})
console.log(new Date().toLocaleTimeString() + ' first init');
setTimeout(function () {
d1.resolve(new Date().toLocaleTimeString() + ' fire resolve');
}, 2000);
setTimeout(function () {
d1.reject(new Date().toLocaleTimeString() + ' first reject');
}, 3000);
var num = 0;
setInterval(function () {
d1.notify(new Date().toLocaleTimeString() + ' first notify')
}, 1000);
下面是结果:
是的,成功了!
jQuery中的deferred相当复杂和巧妙,实际上我也还没有完全理解它的代码,写出这样代码的人真是伟大!
好了,最简单的deferred已经实现了,但是这样似乎太简单了点,下面,我要为她实现一个promise
promise
其实实现promise很简单,只需要将deferred上的done,fail和progress方法绑定到promise方法的返回值上就行了.
,需要注意的是,由于这些函数中的this值的是原来的deferred对象,但是将这些方法绑定到promise返回值的时候,this就不再是deferred对象了,所以我们需要重新制定这些函数列表
promise: function () {
var that = this;
return {
resolve_list: that.resolve_list,
reject_list: that.reject_list,
notify_list: that.notify_list,
done: that.done,
fail: that.fail,
progress: that.progress
}
}
下面测试
var d2 = new deferred();
d2.promise()
.done(function (data) {
console.log(new Date().toLocaleTimeString() + data);
})
.fail(function (data) {
console.log(new Date().toLocaleTimeString() + data);
})
.progress(function (data) {
console.log(new Date().toLocaleTimeString() + data);
});
console.log(new Date().toLocaleTimeString() + ' ' + 'first init');
setTimeout(function () {
d2.resolve(' first resolve');
}, 3000);
setTimeout(function () {
d2.reject(' first reject');
}, 2000);
setInterval(function () {
d2.notify(' first notify');
}, 1000);
测试结果
好了,这个简单版的deferred,它实在是太简化了.
在Deferred在jQuery和Angular中的使用与简单实现(二) 中 我会添加when 方法和then方法以及其他jQuery中的大部分方法. 最后会整合到JavaScript框架设计模块中,
代码 我会托管到github中.
Deferred在jQuery和Angular中的使用与简单实现的更多相关文章
- 深入理解jQuery、Angular、node中的Promise
最初遇到Promise是在jQuery中,在jQuery1.5版本中引入了Deferred Object,这个异步队列模块用于实现异步任务和回调函数的解耦.为ajax模块.队列模块.ready事件提供 ...
- angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了
一.从function JQLite(element)函数开始. function JQLite(element) { if (element instanceof JQLite) { //情况1 r ...
- angular中实现jQuery的Document Ready
angular中不推荐混用JQuery的,原因呢问度娘. 其实这是一个比较蛋疼的问题,尤其是angular2.0,尽量不要在页面上写js,用ts写到模块里面去吧.. 汲取各位先人的智慧,还是列一下 w ...
- angular中的$q.defer()服务异步处理
jquery和angular都有defer服务,我暂以angular为例谈谈我的理解,最后并附上jquery的阮一峰总结的defer. 以我目前项目的部分代码为例说明为什么要用deferred. fu ...
- Angular中的$q的形象解释及深入用法
作者:寸志链接:https://zhuanlan.zhihu.com/p/19622332来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 早上,老爸说:“儿子,天气如何 ...
- Angular中ngCookies模块介绍
1.Cookie介绍 Cookie总是保存在客户端中,按在客户端中的存储位置,可分为内存Cookie和硬盘Cookie.内存Cookie由浏览器维护,保存在内存中,浏览器关闭后就消失了,其存在时间是短 ...
- angular中的compile和link函数
angular中的compile和link函数 前言 这篇文章,我们将通过一个实例来了解 Angular 的 directives (指令)是如何处理的.Angular 是如何在 HTML 中找到这些 ...
- angular源码分析:angular中$rootscope的实现——scope的一生
在angular中,$scope是一个关键的服务,可以被注入到controller中,注入其他服务却只能是$rootscope.scope是一个概念,是一个类,而$rootscope和被注入到cont ...
- 形象的讲解angular中的$q与promise(转)
以下内容摘自http://www.ngnice.com/posts/126ee9cf6ddb68 promise不是angular首创的,作为一种编程模式,它出现在……1976年,比js还要古老得多. ...
随机推荐
- WIN32 窗口类封装 框架实现部分
上面已经讲了窗口封装部分,内容可点击:http://www.cnblogs.com/mengdejun/p/4010320.html,下面分享框架部分内容,完成WINDOWS消息迭代 CQFrameW ...
- Webform Session、Cookies传值,跳转页面方式
Session:每个独立的浏览器都会创建一个独立的Session,不是一台电脑一个Session 存放位置:服务器上 作用:只要里面有内容,那么这个网站中所有的C#端都能访问到这个变量 优点:安全,速 ...
- System.Dynamic.ExpandoObject 类型的简单使用
该类型可以实现的是动态添加属性和移除属性,有点类似 js 中对象的操作,非常灵活 static void Main(string[] args) { dynamic obj = new System. ...
- 【转】Polya定理
转自:http://endlesscount.blog.163.com/blog/static/82119787201221324524202/ Polya定理 首先记Sn为有前n个正整数组成的集合, ...
- GiuHub 使用
一 Mac 能不能连接安卓手机 1 USB数据线 设置 > 通用 > 开发人员选项 > USB调试 > 选择"相机PTP模式" 连接后,手机中的照片和视 ...
- jQuery下的轮播
以前用js做过轮播 今天用JQ插件是最基本的 在官网可以下 布局:<body><div id="div1"> <ul id="lunbo&q ...
- 简单的ATM取款过程
一个简单的ATM的取款过程是这样的:首先提示用户输入密码(pwd),最多只能输3次,超过三次则提示用户“密码已输入三次错误,请取卡.“结束交易.如果用户密码正确,在提示用户输入金额(money),AT ...
- 移动Web触控事件总结
移动web风风火火几多年,让我这个在Pc端漂流的前端er不免心生仰慕,的确入行几多年,也该是时候进军移动web了.移动web中踩到的第一个坑就是事件问题,所以在吸取众大神的经验后,特作总结以示后来者. ...
- ASP.NET Web API标准的“管道式”设计
ASP.NET Web API的核心框架是一个消息处理管道,这个管道是一组HttpMessageHandler的有序组合.这是一个双工管道,请求消息从一端流入并依次经过所有HttpMessageHan ...
- ASP.NET MVC 5 - 控制器
MVC代表: 模型-视图-控制器 .MVC是一个架构良好并且易于测试和易于维护的开发模式.基于MVC模式的应用程序包含: · Models: 表示该应用程序的数据并使用验证逻辑来强制实施业务规则的数据 ...