给你一个承诺 - 玩转 AngularJS 的 Promise(转)
在谈论Promise
之前我们要了解一下一些额外的知识;我们知道JavaScript语言的执行环境是“单线程”,所谓单线程,就是一次只能够执行一个任务,如果有多个任务的话就要排队,前面一个任务完成后才可以继续下一个任务。
这种“单线程”的好处就是实现起来比较简单,容易操作;坏处就是容易造成阻塞,因为队列中如果有一个任务耗时比较长,那么后面的任务都无法快速执行,或导致页面卡在某个状态上,给用户的体验很差。
当然JavaScript提供了“异步模式”去解决上述的问题,关于“异步模式”JavaScript提供了一些实现的方法。
- 回调函数(callbacks)
- 事件监听
- Promise对象
关于回调函数,大家应该都不陌生,比如下面的代码(注:引用Leancloud上面的一点代码):
- AV.User.logIn("myname", "mypass", {
- success: function(user) {
- // Do stuff after successful login.
- },
- error: function(user, error) {
- // The login failed. Check error to see why.
- }
- });
用户通过用户名和密码来进行登录,如果登陆成功的话,会在success
这个模块进行处理,如果登陆失败的话,就会在error
这个模块进行处理。
当我们需要处理的任务不是很多的情况下,使用回调函数还是可以应付的,也没有太大的问题,但是当我们需要处理的任务比较多的时候,使用回调函数的弊端越来越明显了;首先,回调使得调用不一致,得不到保证;当依赖于其它回调时,它们篡改代码的流程,是调试变得异常艰难,每一步调用之后都需要显式的处理错误;最后,过多的回调使得代码的可读性和可维护性都变得很差,所以越来越多的程序员选择使用Promise
去处理异步模式。
关于Promise
我们会在下面进行详细的说明。
Promise
是什么
Promise
是一种异步方式处理值(或者非值)的方法,promise
是对象,代表了一个函数最终可能的返回值或者抛出的异常。
在与远程对象打交道时,Promise
会非常有用,可以把它们看作远程对象的一个代理。
点击下面的链接可以查看Promise
更多的信息
使用Promise
的理由
- 使用
Promise
可以让我们逃脱回调地狱,使我们的代码看起来像是同步的那样。 - 可以在程序中的任何位置捕捉错误,并且绕过依赖于程序异常的的后续代码,获得功能组合和错误冒泡的能力,最重要的是保持了异步运行的能力。
- 使我们的代码的可读性与可维护性都变得很好。
如何在AngularJS
中使用Promise
要在AngularJS
中使用Promise
,要使用AngularJS
的内置服务$q
。
$q
服务受到Kris Kowal的Q
库的启发,所以类似于那个库,但是并没有包含那个库的所用功能。$q
是跟AngularJS
的$rootScope
模板集成的,所以在AngularJS
中执行和拒绝都很快。$q promise
是跟AngularJS
模板引擎集成的,这意味着在视图中找到任何Promise
都会在视图中被执行或者拒绝。
我们可以先使用$q
的defer()
方法创建一个deferred
对象,然后通过deferred
对象的promise
属性,将这个对象变成一个promise
对象;这个deferred
对象还提供了三个方法,分别是resolve()
,reject()
,notify()
。
下面我们来通过代码逐步地将上面的功能都实现,毕竟说得再多,不如你实实在在地把它们敲成代码去实现。
Test1
我们先通过一个同步的例子来创建一个promise
对象。
HTML代码:
- <div ng-app="MyApp">
- <div ng-controller="MyController">
- <label for="flag">成功
- <input id="flag" type="checkbox" ng-model="flag" /><br/>
- </label>
- <hr/>
- <button ng-click="handle()">点击我</button>
- </div>
- </div>
JS代码:
- angular.module("MyApp", [])
- .controller("MyController", ["$scope", "$q", function ($scope, $q) {
- $scope.flag = true;
- $scope.handle = function () {
- var deferred = $q.defer();
- var promise = deferred.promise;
- promise.then(function (result) {
- alert("Success: " + result);
- }, function (error) {
- alert("Fail: " + error);
- });
- if ($scope.flag) {
- deferred.resolve("you are lucky!");
- } else {
- deferred.reject("sorry, it lost!");
- }
- }
- }]);
我们来详细的分析一下上面的代码,我们在html
页面上添加了一个checkbox
,一个button
目的是为了当我们选中checkbox
和不选中checkbox
时,点击下面的按钮会弹出不同的内容。
var deferred = $q.defer()
这段代码创建了一个deferred
对象,我们然后利用var promise = deferred.promise
创建了一个promise
对象。
我们给给promise
的then
方法传递了两个处理函数,分别处理当promise
被执行的时候以及promise
被拒绝的时候所要进行的操作。
下面的一个if(){}else{}
语句块,包含执行和拒绝deferred promise
,如果$scope.flag
为true
,那么我们就会执行deferred promise
,然后我们给promise
传递一个值,也可能是一个对象,表明promise
执行的结果。如果$scope.flag
为false
,那么我们就会拒绝deferred promise
,然后我们给promise
传递一个值,也可能是一个对象,表明promise
被拒绝的原因。
现在回过头来看看,promise
的then
方法,如果promise
被执行,那么它的参数中的第一个函数的result
就代表了"you are lucky!"
我们暂时用的是同步的模式,为的是能够说明问题,后面将会使用异步的方法。
到这里我们可以了解一下$q
的defer()
方法创建的对象具有哪些方法
resolve(value)
:用来执行deferred promise
,value
可以为字符串,对象等。reject(value)
:用来拒绝deferred promise
,value
可以为字符串,对象等。notify(value)
:获取deferred promise
的执行状态,然后使用这个函数来传递它。then(successFunc, errorFunc, notifyFunc)
:无论promise
是成功了还是失败了,当结果可用之后,then
都会立刻异步调用successFunc
,或者'errorFunc',在promise
被执行或者拒绝之前,notifyFunc
可能会被调用0到多次,以提供过程状态的提示。catch(errorFunc)
finally(callback)
通过使用then
进行链式请求
我们通过使用then
方法来进行链式调用,这样做的好处是,无论前一个任务或者说then
函数是被执行或者拒绝了都不会影响后面的then
函数的运行。
我们可以通过then
创建一个执行链,它允许我们中断基于更多功能的应用流程,可以借此导向不同的的结果,这个中断可以让我们在执行链的任意时刻暂停后者推迟promise
的执行。
Test2
HTML代码
- <div ng-app="MyApp">
- <div ng-controller="MyController">
- <label for="flag">成功
- <input id="flag" type="checkbox" ng-model="flag" /><br/>
- </label>
- <div ng-cloak>
- {{status}}
- </div>
- <hr/>
- <button ng-click="handle()">点击我</button>
- </div>
- </div>
JS代码:
- angular.module("MyApp", [])
- .controller("MyController", ["$scope", "$q", function ($scope, $q) {
- $scope.flag = true;
- $scope.handle = function () {
- var deferred = $q.defer();
- var promise = deferred.promise;
- promise.then(function (result) {
- result = result + "you have passed the first then()";
- $scope.status = result;
- return result;
- }, function (error) {
- error = error + "failed but you have passed the first then()";
- $scope.status = error;
- return error;
- }).then(function (result) {
- alert("Success: " + result);
- }, function (error) {
- alert("Fail: " + error);
- })
- if ($scope.flag) {
- deferred.resolve("you are lucky!");
- } else {
- deferred.reject("sorry, it lost!");
- }
- }
- }]);
我们在Part1代码的基础上添加了一些代码,在原来的promise
的链条上新添加了一个then()
处理函数,目的就是为了创建一个执行连,看看在这条执行连上,promise
是如何被执行的。
需要注意的一点是,在第一个then()
方法中,我们在第一个successFunc
函数中将result
的值进行了改变,在第二个errorFunc
函数中对error
的值也进行了改变。
因为这个promise
对象是贯穿整个执行链条的,所以在第一个then()
方法中对其值进行改变必然会反映到后面的then()
方法中
一个异步模式的实例
Test3
第三个例子,我们创建了一个服务,然后在这个服务中创建了一个promise
,服务的目的就是为了拉取github
上面关于angularjs
一些pull
的数据,详细的代码可以看下面
下面的例子包含的部分有点多,因为我是在以前的例子上做的改动,大家可以只看promise
这部分。
目录结构:
- MyApp
- js
- app.js
- controller.js
- service.js
- views
- home.html
- index.html
- js
js/app.js
- angular.module("MyApp", ["ngRoute","MyController", "MyService"])
- .config(["$routeProvider", function($routeProvider){
- $routeProvider
- .when('/',{
- templateUrl: "views/home.html",
- controller: "IndexController"
- });
- }]);
js/controller.js
- angular.module("MyController", [])
- .controller("IndexController", ["$scope", "githubService", function($scope, githubService){
- $scope.name = "dreamapple";
- $scope.show = true;
- githubService.getPullRequests().then(function(result){
- $scope.data = result;
- },function(error){
- $scope.data = "error!";
- },function(progress){
- $scope.progress = progress;
- $scope.show = false;
- });
- }]);
js/service.js
- angular.module("MyService", [])
- .factory('githubService', ["$q", "$http", function($q, $http){
- var getPullRequests = function(){
- var deferred = $q.defer();
- var promise = deferred.promise;
- var progress;
- $http.get("https://api.github.com/repos/angular/angular.js/pulls")
- .success(function(data){
- var result = [];
- for(var i = 0; i < data.length; i++){
- result.push(data[i].user);
- progress = (i+1)/data.length * 100;
- deferred.notify(progress);
- }
- deferred.resolve(result);
- })
- .error(function(error){
- deferred.reject(error);
- });
- return promise;
- }
- return {
- getPullRequests: getPullRequests
- };
- }]);
views/home.html
- <h1>{{name}}</h1>
- <h2>Progress: {{progress}}</h2>
- <h3 ng-show="show">Please wait a moment...</h3>
- <p ng-repeat="person in data">{{person.login}}</p>
index.html
- <!-- 不把下面的注释掉会出现问题,我是指上传到segmentfault上 -->
- <!-- <head>
- <meta charset="UTF-8">
- <title>Route</title>
- <script src="http://cdn.bootcss.com/angular.js/1.4.0-rc.1/angular.js"></script>
- <script src="../node_modules/angular-route/angular-route.js"></script>
- <script src="js/app.js"></script>
- <script src="js/controller.js"></script>
- <script src="js/service.js"></script>
- </head> -->
- <body ng-app="MyApp">
- <header>
- <h1>Header</h1>
- <hr/>
- </header>
- <div ng-view>
- </div>
- <footer>
- <hr/>
- <h1>Footer</h1>
- </footer>
- </body>
http://segmentfault.com/a/1190000002788733
给你一个承诺 - 玩转 AngularJS 的 Promise(转)的更多相关文章
- 【给你一个承诺 - 玩转 AngularJS 的 Promise】
了解Promise 在谈论Promise之前我们要了解一下一些额外的知识:我们知道JavaScript语言的执行环境是"单线程",所谓单线程,就是一次只能够执行一个任务,如果有多个 ...
- Angularjs,WebAPI 搭建一个简易权限管理系统 —— Angularjs名词与概念(一)
目录 前言 Angularjs名词与概念 Angularjs 基本功能演示 系统业务与实现 WebAPI项目主体结构 Angularjs 前端主体结构 2. 前言 Angularjs开发CRUD类型的 ...
- ”危险“的RESTRICT与GCC的编译优化(编程者对编译器所做的一个“承诺”:使用restrict修饰过的指针,它所指向的内容只能经由该指针修改)
restrict是C99标准中新添加的关键字,对于从C89标准开始起步学习C语言的同学来说(包括我),第一次看到restrict还是相当陌生的.Wikipedia给出的解释如下: In the C p ...
- 一起学习造轮子(一):从零开始写一个符合Promises/A+规范的promise
本文是一起学习造轮子系列的第一篇,本篇我们将从零开始写一个符合Promises/A+规范的promise,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Pr ...
- Angularjs,WebAPI 搭建一个简易权限管理系统 —— Angularjs 前端主体结构(五)
目录 前言 Angularjs名词与概念 Angularjs 基本功能演示 系统业务与实现 WebAPI项目主体结构 Angularjs 前端主体结构 6 Angularjs 前端主体结构 6.1 A ...
- 一招制敌 - 玩转 AngularJS 指令的 Scope (作用域),讲得特别好
学习了AngularJS挺长时间,最近再次回首看看指令这部分的时候,觉得比自己刚开始学习的时候理解的更加深入了,尤其是指令的作用域这部分. 步入正题: 每当一个指令被创建的时候,都会有这样一个选择,是 ...
- 贪玩ML系列之一个BP玩一天
手写串行BP算法,可调batch_size 既要:1.输入层f(x)=x 隐藏层sigmoid 输出层f(x)=x 2.run函数实现单条数据的一次前馈 3.train函数读入所有数据for循环处理 ...
- 玩转angularJs——通过自定义ng-model,不仅仅只是input可以实现双向数据绑定
体验更优排版请移步原文:http://blog.kwin.wang/programming/angularJs-user-defined-ngmodel.html angularJs双向绑定特性在开发 ...
- AngularJs $q promise
angularjs提供的$q服务是对Promises规范的一个实现.$q服务可以把一段异步的代码封装成同步的样式. 为啥是样式,因为异步还是异步,它并不会柱塞代码,只是看起来像同步代码. $q.whe ...
随机推荐
- 【转载】OpenStack Swift学习笔记
免责声明: 本文转自网络文章,转载此文章仅为个人收藏,分享知识,如有侵权,请联系博主进行删除. 原文作者:崔炳华 原文地址:http://blog.csdn.net/i_ch ...
- ORA-01031:insufficient privileges
描述:oracle11g用scott用户在plsql上以sysdba身份登录显示以上错误,可是在cmd面板中却正常,网上各种找答案不没有对症,最后这位网友的回答解决了我的问题. 原帖网址:http:/ ...
- Linux查看系统基本信息
问题描述: 查看系统基本信息 问题解决: (1)lspci 是一个用来显示系统中所有PCI总线设备或连接到该总线上的所有设备的工具. 用法: lspci -v (1.1) ...
- java和javascript真的有关系=。=
相同点:1. 内存管理,两者都采用GC来对内存进行回收.因此Java与javascript的内存泄露情况十分相似. 2. 代码编译为机器码后由中间件执行:Java使用前会编译为字节码后由JVM执行,V ...
- 将Windows上的文件上传到Linux上
下载一个SSH Secure Shell Client即可. SSHSecureShellClient-3.2.9下载地址: 免费下载地址在 http://linux.linuxidc.com/ 用户 ...
- 解决unity3d发布的网页游戏放到服务器上无法使用的问题
http://www.unity蛮牛.com/blog-2429-1226.html 第一次把unity3d发布的网页游戏放到服务器上(Win2003),发现无法使用.可以尝试以下办法. ...
- Unity3D脚本中文系列教程(十六)
Unity3D脚本中文系列教程(十五) ◆ function OnPostprocessAudio (clip:AudioClip):void 描述:◆ function OnPostprocess ...
- StringBuffer 和 StringBuilder
如果你读过<Think in Java>,而且对里面描述HashTable和HashMap区别的那部分章节比较熟悉的话,你一定也明白了原因所在.对,就是支持线程同步保证线程安全而导致性能下 ...
- hdu 4704 Sum
思路:对于给定的n,s(i)即将n分解为i个数的组合数,也就是在n-1个位置插入i-1个板即C(n-1,i-1); ∑S=2^(n-1); phi(1000000007)=1000000006; 对于 ...
- IText 生成页脚页码
做doc文档报表的时候可能遇到这样的需求: 每一个页面需要页码,用IText可以完成这样的需求. IText生成doc文档需要三个包:iTextAsian.jar,iText-rtf-2.1.4.ja ...