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操作系统, ...
随机推荐
- iPhone全部设备分辨率速查
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交 ...
- 与MP3相关的技术总结
MP3文件格式解析 Peter Lee 2008-06-05 目录 33 B7 00 0001 02 FF FF FF F4 E1 2F FF FF FFFF DF FF FF FB52 8C 12 ...
- Android软件设置自动检查更新
如果让我推荐功能强大的第三方集成开发包,我一定会推荐友盟,有着强大的软件统计,分析功能(原谅我,我不是打广告). 这一篇介绍友盟的自动更新功能,但是首先你得拥有友盟. 友盟的集成步骤 1.1 导入SD ...
- UNIX网络编程——关于socket阻塞与非阻塞情况下的recv、send、read、write返回值
1.阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有 区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,特别:返回 ...
- pyinstaller使用-python项目转换成exe可执行文件
http://blog.csdn.net/pipisorry/article/details/50620122 Python不是每个人的计算机里面都有安装,当您写了一个好用的工具,需要一个standa ...
- mxgraph进阶(三)Web绘图——mxGraph项目实战(精华篇)
Web绘图--mxGraph项目实战(精华篇) 声明 本文部分内容所属论文现已发表,请慎重对待. 需求 由于小论文实验需求,需要实现根据用户日志提取出行为序列,然后根据行为序列生成有向图的形式 ...
- 学生信息管理小系统(以XML为存储方式)
为了更好地应用XML,就写了这个小项目. 下面是我的项目的目录结构 项目思路 dao是Date Access Object 数据访问层,主要是负责操作数据 domain是实体层,类似于bean层,放置 ...
- Cocos2D:塔防游戏制作之旅(十四)
塔之战:炮塔的攻击 炮塔就位了?检查.敌人前进中?再次检查 - 它们看起来就是如此!看起来到了击溃这些家伙的时候了!这里我们将智能置入炮塔的代码中去. 每一个炮塔检查是否有敌人在其攻击范围.(炮塔一次 ...
- 【Unity Shaders】Using Textures for Effects——让sprite sheets动起来
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
- 看uboot的时候发现随机数的另外一种算法
#include <stdio.h> #include <time.h> static unsigned int y = 1U; unsigned int rand_r(uns ...