angularjs系列之轻松使用$q进行异步编程
第一部分关于js中的异步编程
异步编程简单的说就是你写了一段代码,但他不会按照你书写代码的顺序立即执行,而是等到程序中发生了某个事件(如用户点击了某个按钮,某个ajax请求得到了响应)才去执行这段代码,而且这段代码可能执行一次(如一个ajax请求得到了响应)、也可能执行很多次或者不执行(一个按钮被点击了许多次或者0次)这就是所谓的异步编程。
有两种异步程序模式单次执行模式和监听执行模式。像ajax请求这样的就是属于单次执行模式,请求、回调只会进行一次。像事件绑定就属于监听执行模式,只要事件发生回调就可以不断的执行。但是不管是单次执行模式还是监听执行模式在js程序中的共同点都是保存着一个函数引用(这个引用的表现形式就回调函数),在某个事件发生后开始执行这个函数引用中的代码。
下面给出这两种模式的整体模型图:
异步执行体:包含异步执行代码(或者异步动作)和状态,状态不可逆。
回调执行体:包含回调执行代码和状态,状态不可逆。
两种执行体是依靠异步执行体发送信号通信。
监听执行模式和单次执行模式的区别:监听模式中会有多个异步执行体和回调执行体副本,且每个异步执行体副本关联一个回调执行体副本。多个副本的这两种执行体的执行代码是一样的,唯一区别的就是每个副本的状态不同。
代码举例:
//单次执行模式:
funtion a(b) {
console.log(“a process”);
b();
};
function b() {
console.log(“b process”);
};
a(b); //发送信号
//监听执行模式(这里直接使用jquery):
$(“#button”).bind(“click”, function() {
console.log(“click callback”);
});
$(“#button”).trigger(“click”); //发送信号
第二部分关于promise模式的异步编程
上面就是异步编程的两种形式,由于我们的$q是解决第一种模式中存在的问题的,所以这里就讨论讨论单次执行模式中存在的问题。你可以设想这么一种场景,当你要在很多个ajax请求响应完成后做一件事情,你用现有的js的回调方式,会不会发现回调的层次很深、代码十分混乱。而然我们promise模式的异步编程方式就能很好的管理这些单次执行模式下的代码,使你的代码书写起来更加优雅,说白了promise不产生新的东西只是一个语法糖使你编写出更加优雅的异步程序代码。
那promise模式到底是什么样的呢,说白了就是把把我上面的单次执行模式图抽象化了,用一个defer对象(延期对象)代表异步执行体,用一个promise对象(承诺对象)代表回调执行体,这个defer对象可以发送消息,promise接受消息,然后执行相应的回调函数。Promise就是异步执行体和回调执行体之间的桥梁,这样的好处是异步执行体和回调执行体这种模型很好的对异步动作和回调动作进行了解耦。你可以在promise上面好好的操纵你的回调执行体,而只是接受一个来自defer发送的参数。
这里还不能很好的体现出promise模式的优势,而她真正的优势是在这种模型下扩充的api使其发挥了真正的强大作用。比如说promise对象的then方法,这个方法就是支持异步链式编程最重要的方法,他也是使代码更加优雅最重要的方法。还有比如说all接收一个promise数组返回一个新的promise,当前面的所有promise状态都完成之后这个新的promise才能完成,这个很适合多个ajax后处理某些事情。不过可能你还不能明白,下面在介绍$qAPI的时候有详细的介绍。
第三部分 $q服务的API详解
下面我们通过讲解$q的API让你更多的了解promise异步编程模式。$q是做为angularjs的一个服务而存在的,只是对promise异步编程模式的一个简化实现版,源码中剔除注释实现代码也就二百多行,下面开始介绍$q的API。
defer对象(延迟对象)可以通$q.defer()获取,下面是defer对象的api:
方法:
resolve(value):向promise对象异步执行体发送消息告诉他我已经成功完成任务,value即为发送的消息。
reject(value): 向promise对象异步执行体发送消息告诉他我已经不可能完成这个任务了,value即为发送的消息。
notify(value): 向promise对象异步执行体发送消息告诉他我现在任务完成的情况,value即为发送的消息。
这些消息发送完promise会调用现有的回调函数。
属性:
promise即与这个defer对象的承诺对象。
从上可以看出defer主要是用来发送消息的。
promise对象可以通过defer.promise获取,下面是promise对象的api:
方法:
then(successCallback,errorCallback,notifyCallback):参数为不同消息下的不同回调函数,defer发送不同的消息执行不同的回调函数,消息作为这些回调函数的参数传递。返回值为回一个promise对象为支持链式调用而存在。当第一个defer对象发送消息后,后面的promise对应的defer对象也会发送消息,但是发送的消息不一样,不管第一个defer对象发送的是reject还是resolve,第二个及其以后的都是发送的resolve,消息是可传递的。
catch(errorCallback):then(null,errorCallback)的缩写。
finally(callback):相当于then(callback,callback)的缩写,这个finally中的方法不接受参数,却可以将defer发送的消息和消息类型成功传递到下一个then中。
代码举例:
function f1(num) {
document.write("success:" + (num++) + "<br/>");
return num;
}
function f2(num) {
document.write("errror:" + (num++) + "<br/>");
return num;
}
var defer = $q.defer();
var promise = defer.promise;
//方式1
// promise.then(f1,f2).then(f1,f2);
// 方式2
// promise.then(f1,f2);
// promise.then(f1,f2);
// 方式3
// promise.then(f1,f2).then(f1,f2);
// promise.catch(f2);
// promise.finally(f2);
//方式4
// promise.finally(f2).then(f1,f2);
defer.reject(1);
方式1的结果:
errror: 1
success: 2
方式2的结果:
errror: 1
errror: 1
方式3的结果:
errror: 1
errror: 1
error: NaN
success: 2
方式4的结果:
Error: NaN
Error: 1
现在继续$q的api:
方法:
defer():用来生成一个延迟对象 var defer =$q.defer();
reject():参数接收错误消息,相当于在回调函数中抛出一个异常,然后在下一个then中调用错误的回调函数。
代码举例:
var defer = $q.defer();
var promise = defer.promise;
promise.then(function() {
return $q.reject("success error");
}, function() {
return $q.reject("error error");
}).then(function(info) {
document.write("s:" + info + "<br/>");
}, function(info) {
document.write("e:" + info + "<br/>");
});
//方式1
// defer.reject(1);
//方式2
// defer.resolve(1);
方式1运行结果
e: error error
方式2运行结果
e: success error
all():参数接收为一个promise数组,返回一个新的单一promise对象,当这些promise对象对应defer对象全部解决这个单一promise对象才会解决,当这些promise对象中有一个被reject了,这个单一promise同样的被reject了。
代码举例:
var defer1 = $q.defer();
var promise1 = defer1.promise;
promise1.then(function(num) {
console.log("success" + num);
},
function(num) {
console.log("error" + num);
});
var defer2 = $q.defer();
var promise2 = defer2.promise;
promise1.then(function(num) {
console.log("success" + num);
},
function(num) {
console.log("error" + num);
});
var promise3 = $q.all([promise1, promise1]);
promise3.then(function(num) {
console.log("s:" + num);
}, function(num) {
console.log("e:" + num);
});
//方式1
// defer1.resolve(1);
// defer2.resolve(1);
//方式2
//defer1.reject(1);
方式1的结果:
success1
success2: 1
s: 1,
1
方式2的结果:
error1
e: 1
when():接收第一个参数为一个任意值或者是一个promise对象,其他3个同promise的then方法,返回值为一个promise对象。第一个参数若不是promise对象则直接运行success回调且消息为这个对象,若为promise那么返回的promise其实就是对这个promise类型的参数的一个包装而已,被传入的这个promise对应的defer发送的消息,会被我们when函数返回的promise对象所接收到。
代码举例:
var promise = $q.when(1, function(num) {
console.log("s" + num);
}, function() {
console.log("e");
});
var defer1 = $q.defer();
var promise1 = defer1.promise;
var promise2 = $q.when(promise1, function(num) {
console.log("s" + num);
}, function() {
console.log("e");
});
defer1.reject(1);
运行结果:
s1
e
对上面还有一个注意事项就是defer对象发送消息不会立即执行的,而是把要执行的代码放到了rootScope的evalAsync队列当中,当时scope.$apply的时候才会被promise接收到这个消息。
到这里$q服务全部介绍完了,对于angular中的then的链式调用中如果defer发送的reject的那么只有第一个promise是reject的回调,其他的都是resolve的回调这里多少觉得是不合适的,不知道是个bug还是就是这样设计?$q只适合单次执行模式,不知道是否适合扩展成监听执行模式?这都是大家值得思考的问题。
angularjs系列之轻松使用$q进行异步编程的更多相关文章
- Promise和异步编程
前面的话 JS有很多强大的功能,其中一个是它可以轻松地搞定异步编程.作为一门为Web而生的语言,它从一开始就需要能够响应异步的用户交互,如点击和按键操作等.Node.js用回调函数代替了事件,使异步编 ...
- Java8函数之旅 (八) - 组合式异步编程
前言 随着多核处理器的出现,如何轻松高效的进行异步编程变得愈发重要,我们看看在java8之前,使用java语言完成异步编程有哪些方案. JAVA8之前的异步编程 继承Thead类,重写run方法 实现 ...
- angularjs 系列之$q和promise
还是同一个项目,在项目中,发现多个controller之内有一个共同的服务器请求,当时只是不断的重复使用,如今,现在项目结束,代码开始走向了优化迭代的阶段: 首先,我的思路是把这个共同的请求,从con ...
- angularJS中的Promise对象($q)的深入理解
原文链接:a better way to learn AngularJS - promises AngularJS通过内置的$q服务提供Promise编程模式.通过将异步函数注册到promise对象, ...
- AngularJS 系列 学习笔记 目录篇
目录: AngularJS 系列 01 - HelloWorld和数据绑定 AngularJS 系列 02 - 模块 (持续更新)
- AngularJS 系列 01 - HelloWorld和数据绑定
目录导读: AngularJS 系列 学习笔记 目录篇 前言: 好记性不如烂键盘,随笔就是随手笔记,希望以后有用. 本篇目录: 1. Hello World 2. AngularJS中的数据绑定 3. ...
- AngularJS 系列 02 - 模块
引导目录: AngularJS 系列 学习笔记 目录篇 前言: 其实,在上篇文章介绍数据绑定的时候,我们的HelloWorld的代码案例中就已经使用了模块(module).哈哈. 本篇就着重介绍一下a ...
- C#基础系列——异步编程初探:async和await
前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法.确实,没有异步的多线程是单调的.乏味的,async和await是出现在C#5.0之后,它的出现给了 ...
- WPF技术触屏上的应用系列(五): 图片列表异步加载、手指进行缩小、放大、拖动 、惯性滑入滑出等效果
原文:WPF技术触屏上的应用系列(五): 图片列表异步加载.手指进行缩小.放大.拖动 .惯性滑入滑出等效果 去年某客户单位要做个大屏触屏应用,要对档案资源进行展示之用.客户端是Window7操作系统, ...
随机推荐
- 【OpenGL】详解第一个OpenGL程序
写在前面 OpenGL能做的事情太多了!很多程序也看起来很复杂.很多人感觉OpenGL晦涩难懂,原因大多是被OpenGL里面各种语句搞得头大,一会gen一下,一会bind一下,一会又active一下. ...
- Java基础---Java---基础加强---内省的简单运用、注解的定义与反射调用、 自定义注解及其应用、泛型及泛型的高级应用、泛型集合的综合
内省的简单运用: JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则. 采用遍历BeanInfo的所有属性方式来查找和 ...
- Nginx创建密码保护目录
nginx 的根目录 为:/home/undoner/nginx-wwwnginx 访问地址 为:http://127.0.0.1本文实现对nginx根目录文件访问的权限控制 (1)nginx指定密码 ...
- Windows Server2008R2、2012R2重置系统开机登陆密码
平时用的虚拟机太多导致经常会忘记密码,这里分享两个链接,分别对应的是08R2和12R2重置密码的方法. 08R2:http://ucweb.blog.51cto.com/4042188/962284 ...
- Mybatis源码之Statement处理器SimpleStatementHandler(四)
SimpleStatementHandler就是使用基本的Statement来执行query.batch.update等操作,其实现还是比较简单的,当然在执行过程中会涉及keyGenerator和Re ...
- Android开发-Listview中显示不同的视图布局
1. 使用场景 在重写ListView的BaseAdapter时,我们常常在getView()方法中复用convertView,以提高性能.convertView在Item为单一的同种类型布局时,能够 ...
- JAVA内部类_1
使用内部类的原因: (1)可以访问该类定义所在作用域中的数据,包括私有数据. (2)可以对同一个包中的其它类隐藏起来. (3)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷. 下面 ...
- React Native之AppRegistry模块
我们在写react native的js的时候,在最后总会加上一段代码: AppRegistry.registerComponent('ReactDemo', () => ReactDemo); ...
- MySQL数据库写入图片并读取图片显示到JLabel上的详解
相较于Oracle,MySQL作为一个轻量级的开源的数据库,可谓是大大简化了我们的操作.这次我就来写一个关于数据库存入图片,获取图片的例子吧,也为了今后的复习使用.(我们一般采取存入路径的方式,而不是 ...
- (NO.00001)iOS游戏SpeedBoy Lite成形记(二十五)
每次压赌要打开弹出菜单还是让人略觉不爽,下面我们再添加一个随机押注的按钮:自动随机选择选手和下赌金额. 打开spriteBuilder,修改GameInterface.ccb的界面,在Run按钮旁边添 ...