了解Promise
在谈论Promise 之前我们要了解一下一些额外的知识;我们知道JavaScript语言的执行环境是“单线程”,所谓单线程,就是一次只能够执行一个任务,如果有多个任务的话就要排队,前面一个任务完成后才可以继续下一个任务。
这种“单线程”的好处就是实现起来比较简单,容易操作;坏处就是容易造成阻塞,因为队列中如果有一个任务耗时比较长,那么后面的任务都无法快速执行,或导致页面卡在某个状态上,给用户的体验很差。
当然JavaScript提供了“异步模式”去解决上述的问题,关于“异步模式”JavaScript提供了一些实现的方法。
- 回调函数(callbacks)
- 事件监听
- Promise对象
关于回调函数,大家应该都不陌生,比如下面的代码(注:引用Leancloud上面的一点代码):
AV.User.logIn("myname","mypass",{
success: function(user) {
},error: function(user,error) {
}
});
用户通过用户名和密码来进行登录,如果登陆成功的话,会在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)
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",$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
- 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","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
<body ng-app="MyApp">
<header>
<h1>Header</h1>
<hr/>
</header>
<div ng-view>
</div>
<footer>
<hr/>
<h1>Footer</h1>
</footer>
</body>
终于可以伸个懒腰了,关于$q 还有一个方法,大家有兴趣的话可以自己看看相关资料,我这里就不多说了。。。
如果你觉得这篇文章哪里说得不正确,欢迎大家指出来,一起进步!^_^ (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|