您的位置:首页 > Web前端 > AngularJS

AngularJS 的 $q 和 Promise

2016-06-12 14:57 645 查看


了解
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
更多的信息
Promises/A+
Promises/A


使用
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)


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
<code style="font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', monospace; font-size: 1em; color: inherit; padding: 0px; white-space: inherit; background-image: none; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">h1</span>></span>{{name}}<span class="hljs-tag" style="color: rgb(0, 102, 102);"></<span class="hljs-title" style="color: rgb(38, 139, 210);">h1</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">h2</span>></span>Progress: {{progress}}<span class="hljs-tag" style="color: rgb(0, 102, 102);"></<span class="hljs-title" style="color: rgb(38, 139, 210);">h2</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">h3</span> <span class="hljs-attribute" style="color: rgb(181, 137, 0);">ng-show</span>=<span class="hljs-value" style="color: rgb(42, 161, 152);">"show"</span>></span>Please wait a moment...<span class="hljs-tag" style="color: rgb(0, 102, 102);"></<span class="hljs-title" style="color: rgb(38, 139, 210);">h3</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">p</span> <span class="hljs-attribute" style="color: rgb(181, 137, 0);">ng-repeat</span>=<span class="hljs-value" style="color: rgb(42, 161, 152);">"person in data"</span>></span>{{person.login}}<span class="hljs-tag" style="color: rgb(0, 102, 102);"></<span class="hljs-title" style="color: rgb(38, 139, 210);">p</span>></span>
</code>


index.html
<code style="font-family: 'Source Code Pro', Consolas, Menlo, Monaco, 'Courier New', monospace; font-size: 1em; color: inherit; padding: 0px; white-space: inherit; background-image: none; background-position: initial initial; background-repeat: initial initial;">
<span class="hljs-comment" style="color: rgb(147, 161, 161);"><!-- 不把下面的注释掉会出现问题,我是指上传到segmentfault上 --></span>
<span class="hljs-comment" style="color: rgb(147, 161, 161);"><!-- <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> --></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">body</span> <span class="hljs-attribute" style="color: rgb(181, 137, 0);">ng-app</span>=<span class="hljs-value" style="color: rgb(42, 161, 152);">"MyApp"</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">header</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">h1</span>></span>Header<span class="hljs-tag" style="color: rgb(0, 102, 102);"></<span class="hljs-title" style="color: rgb(38, 139, 210);">h1</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">hr</span>/></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"></<span class="hljs-title" style="color: rgb(38, 139, 210);">header</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">div</span> <span class="hljs-attribute" style="color: rgb(181, 137, 0);">ng-view</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"></<span class="hljs-title" style="color: rgb(38, 139, 210);">div</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">footer</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">hr</span>/></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(38, 139, 210);">h1</span>></span>Footer<span class="hljs-tag" style="color: rgb(0, 102, 102);"></<span class="hljs-title" style="color: rgb(38, 139, 210);">h1</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"></<span class="hljs-title" style="color: rgb(38, 139, 210);">footer</span>></span>
<span class="hljs-tag" style="color: rgb(0, 102, 102);"></<span class="hljs-title" style="color: rgb(38, 139, 210);">body</span>></span></code>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: