Promise
在谈论Promise
以前咱们要了解一下一些额外的知识;咱们知道JavaScript语言的执行环境是“单线程”,所谓单线程,就是一次只可以执行一个任务,若是有多个任务的话就要排队,前面一个任务完成后才能够继续下一个任务。javascript
这种“单线程”的好处就是实现起来比较简单,容易操做;坏处就是容易形成阻塞,由于队列中若是有一个任务耗时比较长,那么后面的任务都没法快速执行,或致使页面卡在某个状态上,给用户的体验不好。css
固然JavaScript提供了“异步模式”去解决上述的问题,关于“异步模式”JavaScript提供了一些实现的方法。html
关于回调函数,你们应该都不陌生,好比下面的代码(注:引用Leancloud上面的一点代码):java
javascriptAV.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
这个模块进行处理。node
当咱们须要处理的任务不是不少的状况下,使用回调函数仍是能够应付的,也没有太大的问题,可是当咱们须要处理的任务比较多的时候,使用回调函数的弊端愈来愈明显了;首先,回调使得调用不一致,得不到保证;当依赖于其它回调时,它们篡改代码的流程,是调试变得异常艰难,每一步调用以后都须要显式的处理错误;最后,过多的回调使得代码的可读性和可维护性都变得不好,因此愈来愈多的程序员选择使用Promise
去处理异步模式。git
关于Promise
咱们会在下面进行详细的说明。程序员
Promise
是什么Promise
是一种异步方式处理值(或者非值)的方法,promise
是对象,表明了一个函数最终可能的返回值或者抛出的异常。angularjs
在与远程对象打交道时,Promise
会很是有用,能够把它们看做远程对象的一个代理。github
点击下面的连接能够查看Promise
更多的信息segmentfault
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()
。
下面咱们来经过代码逐步地将上面的功能都实现,毕竟说得再多,不如你实实在在地把它们敲成代码去实现。
咱们先经过一个同步的例子来建立一个promise
对象。
HTML代码:
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代码:
javascriptangular.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
的执行。
HTML代码
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代码:
javascriptangular.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()
方法中
第三个例子,咱们建立了一个服务,而后在这个服务中建立了一个promise
,服务的目的就是为了拉取github
上面关于angularjs
一些pull
的数据,详细的代码能够看下面
下面的例子包含的部分有点多,由于我是在之前的例子上作的改动,你们能够只看promise
这部分。
目录结构:
js/app.js
javascriptangular.module("MyApp", ["ngRoute","MyController", "MyService"]) .config(["$routeProvider", function($routeProvider){ $routeProvider .when('/',{ templateUrl: "views/home.html", controller: "IndexController" }); }]);
js/controller.js
javascriptangular.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
javascriptangular.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>
终于能够伸个懒腰了,关于$q
还有一个方法,你们有兴趣的话能够本身看看相关资料,我这里就很少说了。。。
若是你以为这篇文章哪里说得不正确,欢迎你们指出来,一块儿进步!^_^