在谈论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 KowalQ库的启发,所以类似于那个库,但是并没有包含那个库的所用功能。
  • $q是跟AngularJS$rootScope模板集成的,所以在AngularJS中执行和拒绝都很快。
  • $q promise是跟AngularJS模板引擎集成的,这意味着在视图中找到任何Promise都会在视图中被执行或者拒绝。

我们可以先使用$qdefer()方法创建一个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对象。

我们给给promisethen方法传递了两个处理函数,分别处理当promise被执行的时候以及promise被拒绝的时候所要进行的操作。

下面的一个if(){}else{}语句块,包含执行和拒绝deferred promise,如果$scope.flagtrue,那么我们就会执行deferred promise,然后我们给promise传递一个值,也可能是一个对象,表明promise执行的结果。如果$scope.flagfalse,那么我们就会拒绝deferred promise,然后我们给promise传递一个值,也可能是一个对象,表明promise被拒绝的原因。

现在回过头来看看,promisethen方法,如果promise被执行,那么它的参数中的第一个函数的result就代表了"you are lucky!"

我们暂时用的是同步的模式,为的是能够说明问题,后面将会使用异步的方法。

到这里我们可以了解一下$qdefer()方法创建的对象具有哪些方法

  • resolve(value):用来执行deferred promisevalue可以为字符串,对象等。
  • reject(value):用来拒绝deferred promisevalue可以为字符串,对象等。
  • notify(value):获取deferred promise的执行状态,然后使用这个函数来传递它。
  • then(successFunc, errorFunc, notifyFunc):无论promise是成功了还是失败了,当结果可用之后,then都会立刻异步调用successFunc,或者'errorFunc',在promise被执行或者拒绝之前,notifyFunc可能会被调用0到多次,以提供过程状态的提示。
  • catch(errorFunc)
  • finally(callback)

    Online Code Part1

通过使用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!");
}
}
}]);

Online Code Part2

我们在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/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(转)的更多相关文章

  1. 【给你一个承诺 - 玩转 AngularJS 的 Promise】

    了解Promise 在谈论Promise之前我们要了解一下一些额外的知识:我们知道JavaScript语言的执行环境是"单线程",所谓单线程,就是一次只能够执行一个任务,如果有多个 ...

  2. Angularjs,WebAPI 搭建一个简易权限管理系统 —— Angularjs名词与概念(一)

    目录 前言 Angularjs名词与概念 Angularjs 基本功能演示 系统业务与实现 WebAPI项目主体结构 Angularjs 前端主体结构 2. 前言 Angularjs开发CRUD类型的 ...

  3. ”危险“的RESTRICT与GCC的编译优化(编程者对编译器所做的一个“承诺”:使用restrict修饰过的指针,它所指向的内容只能经由该指针修改)

    restrict是C99标准中新添加的关键字,对于从C89标准开始起步学习C语言的同学来说(包括我),第一次看到restrict还是相当陌生的.Wikipedia给出的解释如下: In the C p ...

  4. 一起学习造轮子(一):从零开始写一个符合Promises/A+规范的promise

    本文是一起学习造轮子系列的第一篇,本篇我们将从零开始写一个符合Promises/A+规范的promise,本系列文章将会选取一些前端比较经典的轮子进行源码分析,并且从零开始逐步实现,本系列将会学习Pr ...

  5. Angularjs,WebAPI 搭建一个简易权限管理系统 —— Angularjs 前端主体结构(五)

    目录 前言 Angularjs名词与概念 Angularjs 基本功能演示 系统业务与实现 WebAPI项目主体结构 Angularjs 前端主体结构 6 Angularjs 前端主体结构 6.1 A ...

  6. 一招制敌 - 玩转 AngularJS 指令的 Scope (作用域),讲得特别好

    学习了AngularJS挺长时间,最近再次回首看看指令这部分的时候,觉得比自己刚开始学习的时候理解的更加深入了,尤其是指令的作用域这部分. 步入正题: 每当一个指令被创建的时候,都会有这样一个选择,是 ...

  7. 贪玩ML系列之一个BP玩一天

    手写串行BP算法,可调batch_size 既要:1.输入层f(x)=x  隐藏层sigmoid 输出层f(x)=x 2.run函数实现单条数据的一次前馈 3.train函数读入所有数据for循环处理 ...

  8. 玩转angularJs——通过自定义ng-model,不仅仅只是input可以实现双向数据绑定

    体验更优排版请移步原文:http://blog.kwin.wang/programming/angularJs-user-defined-ngmodel.html angularJs双向绑定特性在开发 ...

  9. AngularJs $q promise

    angularjs提供的$q服务是对Promises规范的一个实现.$q服务可以把一段异步的代码封装成同步的样式. 为啥是样式,因为异步还是异步,它并不会柱塞代码,只是看起来像同步代码. $q.whe ...

随机推荐

  1. Java7 新特性 数值文本表示法

    今天和大家分享下 java7中新特性-数值文本表示法 首先,在原来jdk1.6中 如果需要将一个二进制的数值转换成十进制的话,一般情况下都会以下面的代码方式去实现. public static voi ...

  2. float2int

    flaot转int时,会直接舍弃小数为,但是当把f所在的地址的数据当成int解析时,就是另外的情况了. #include<iostream> using namespace std; in ...

  3. POJ2004 Mix and build Trie树? dp?

    学习Trie树中,所以上网搜一下Trie树的题,找到这个,人家写着是简单dp,那我就想着能学习到什么Trie树上的dp,但最后发现根本好像跟Trie树没有什么联系嘛... 题意就是给你很多个字符串(长 ...

  4. Android 近百个项目的源代码

    Android 近百个项目的源代码 Android PDF 阅读器 http://sourceforge.net/projects/andpdf/files/个人记账工具 OnMyMeans http ...

  5. Java运行系统命令并获取值(Process java.lang.Runtime.exec(String[] cmdarray, String[] envp, File dir)

    package test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; ...

  6. Bessie的体重问题

    P1028 Bessie的体重问题 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 USACO OCT09 8TH  描述 Bessie像她的诸多姊妹一样,因 ...

  7. SaaS系列介绍之十五: SaaS知识重用

    1 建立并积累自己的开发体系 遵行业界的规定又有自己的特色是我们所追求的目标.成功的软件公司都有丰富而可复用的代码组件,几行代码在单个系统里可能无足轻重,但一旦可在大量的系统中可重复使用那就是价值不菲 ...

  8. *[hackerrank]ACM ICPC Team

    https://www.hackerrank.com/contests/w6/challenges/acm-icpc-team 这道题在contest的时候数据量改小过,原来的数据量需要进行优化才能过 ...

  9. 国内为什么没有好的 Stack Overflow 的模仿者?

    国内为什么没有好的 Stack Overflow 的模仿者? 个人觉得, 高端的程序员会直接上stackoverflow提问, 所以国内中文的stackoverflow必然面对低端程序员. 鉴于中国程 ...

  10. MSSQLServer基础07(事务,存储过程,分页的存储过程,触发器)

    事务 事务:保证多个操作全部成功,否则全部失败,这处机制就是事务 思考:下了个订单,但是在保存详细信息时出错了,这样可以成功吗? 数据库中的事务:代码全都成功则提交,如果有某一条语句失败则回滚,整体失 ...