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

了解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可以让我们逃脱回调地狱,使我们的代码看起来像是同步的那样。
  • 可以在程序中的任何位置捕捉错误,并且绕过依赖于程序异常的的后续代码,获得功能组合和错误冒泡的能力,最重要的是保持了异步运行的能力。
  • 使我们的代码的可读性与可维护性都变得很好。

如何在AngularJS中使用Promise



要在AngularJS中使用Promise,要使用AngularJS的内置服务$q

  • $q服务受到Kris Kowal的Q库的启发,所以类似于那个库,但是并没有包含那个库的所用功能。
  • $q是跟AngularJS$rootScope模板集成的,所以在AngularJS中执行和拒绝都很快。
  • $q promise是跟AngularJS模板引擎集成的,这意味着在视图中找到任何Promise都会在视图中被执行或者拒绝。

我们可以先使用$qdefer()方法创建一个deferred对象,然后通过deferred对象的promise属性,将这个对象变成一个promise对象;这个deferred对象还提供了三个方法,分别是resolve(),reject(),notify()

下面我们来通过代码逐步地将上面的功能都实现,毕竟说得再多,不如你实实在在地把它们敲成代码去实现。

Test1



我们先通过一个同步的例子来创建一个promise对象。

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>promise</title>
    <script type="text/javascript" src="//cdn.bootcss.com/angular.js/1.5.8/angular.min.js"></script>
    <script type="text/javascript" src="//cdn.bootcss.com/angular.js/1.5.8/angular-route.min.js"></script>
    <script>
        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("成功:"+result);
                    },function(error){
                        alert("失败:"+error);
                    });
                    if($scope.flag){
                        deferred.resolve("you are lucky!");
                    }else{
                        deferred.reject("sorry,it lost!");
                    }
                }
            }])
    </script>
</head>
<body>
    <div ng-app="myApp">
        <div ng-controller="myController">
            <label for="flag">成功
            <input type="checkbox" id="flag" ng-model="flag"><br>
            </label>
            <hr>
            <button ng-click="handle()">点击我</button>
        </div>
    </div>
</body>
</html>

效果演示:http://jsfiddle.net/gwfqga6m/

通过使用then进行链式请求



我们通过使用then方法来进行链式调用,这样做的好处是,无论前一个任务或者说then函数是被执行或者拒绝了都不会影响后面的then函数的运行。

我们可以通过then创建一个执行链,它允许我们中断基于更多功能的应用流程,可以借此导向不同的的结果,这个中断可以让我们在执行链的任意时刻暂停后者推迟promise的执行。

Test2

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>promise</title>
    <script type="text/javascript" src="//cdn.bootcss.com/angular.js/1.5.8/angular.min.js"></script>
    <script type="text/javascript" src="//cdn.bootcss.com/angular.js/1.5.8/angular-route.min.js"></script>
    <script>
        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!");
                        }
                    }
                }]);
    </script>
</head>
<body>
    <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>
</body>
</html>

效果演示:http://jsfiddle.net/gwfqga6m/1/

我们在Part1代码的基础上添加了一些代码,在原来的promise的链条上新添加了一个then()处理函数,目的就是为了创建一个执行连,看看在这条执行连上,promise是如何被执行的。

需要注意的一点是,在第一个then()方法中,我们在第一个successFunc函数中将result的值进行了改变,在第二个errorFunc函数中对error的值也进行了改变。

因为这个promise对象是贯穿整个执行链条的,所以在第一个then()方法中对其值进行改变必然会反映到后面的then()方法中

  



(未完待续...................................................................)

时间: 2024-10-02 21:39:34

【给你一个承诺 - 玩转 AngularJS 的 Promise】的相关文章

给你一个承诺 - 玩转 AngularJS 的 Promise(转)

在谈论Promise之前我们要了解一下一些额外的知识:我们知道JavaScript语言的执行环境是“单线程”,所谓单线程,就是一次只能够执行一个任务,如果有多个任务的话就要排队,前面一个任务完成后才可以继续下一个任务. 这种“单线程”的好处就是实现起来比较简单,容易操作:坏处就是容易造成阻塞,因为队列中如果有一个任务耗时比较长,那么后面的任务都无法快速执行,或导致页面卡在某个状态上,给用户的体验很差. 当然JavaScript提供了“异步模式”去解决上述的问题,关于“异步模式”JavaScrip

随心篇第七期:我是一个爱玩游戏的孩子

我是一个爱玩游戏的孩子,从小时记事起到现在,从没离开过游戏 你可能会问:你爸爸妈妈不管你吗? 好吧,爸爸妈妈有管,但是家里做买卖,比较忙,哪里有空呢 多亏爸爸妈妈没有管我玩游戏,要不然真的会阻止我游戏方面天赋成长的啊 最近正在玩的游戏是炉石和D3,当然了,有一起玩的可以随时来叫我哦 我的战网账号:[email protected] 我玩游戏的时候很认真的,就像我平常做事情一样,很少溜号的,因为那些是不好的习惯,所以我们要抵制才好 我喜欢调研,在游戏中,我擅长玩一些单机游戏,经常会去思考,而不是无

怎样才能成为一个电玩程序员(转)

电玩游戏广受青少年们欢迎,其中不少年轻人专注于此,甚至想到要自己编写一个游戏.另外一方面电玩游戏工程师被大多数人认为是大有"钱"途的.如果你做的游戏走红了,成为百万富翁也是分分钟的事情.要是你果真对电玩有着持续的热情,且立志成为电玩工程师,我这里有心得可以分享.如果你够努力,够诚心,就一定没问题!关键是要对自己有信心. 1.资讯灵通: 经常和相关同行啊前辈们交流,走访校园,多阅读些关于电玩编程的杂志书籍,电子书什么的.这样就能更加了解你将要进入的领域究竟是什么样儿的.同时还能了解到要成

马云演讲:给自己一个梦想,给自己一个承诺,给自己一个坚持!

作为一个创业者,首先要给自己一个梦想,在95年我偶然有了一次机会到了美国,然后我发现了互联网,回来以后,我请了24个朋友到我家里,我说我准备从大学里辞职,做一个互联网,2个小时以后,大家投票表决,24个人,23个反对,1个人支持.大家觉得那个东西肯定不靠谱.你电脑也不懂,根本不存在有这么个网络.但我经过晚上的考虑,第二天早上,我决定我还是辞职,去实现我自己的梦想.为什么是这样呢? 我今天回过来想,我发现很多的年轻人是"晚上想想千条路,早上起来走老路"晚上的时候想,明天我做干这事,第二天

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

学习了AngularJS挺长时间,最近再次回首看看指令这部分的时候,觉得比自己刚开始学习的时候理解的更加深入了,尤其是指令的作用域这部分. 步入正题: 每当一个指令被创建的时候,都会有这样一个选择,是继承自己的父作用域(一般是外部的Controller提供的作用域或者根作用域($rootScope)),还是创建一个新的自己的作用域,当然AngularJS为我们指令的scope参数提供了三种选择,分别是:false,true,{}:默认情况下是false. scope = false 首先我们来看

写一下自己一个星期玩树莓派的经验,如何安装系统,如何在树莓派中安装opencv,如何运行代码。

在树莓派上安装opencv最简单的方法是: sudo apt-get update sudo apt-get install libopencv-dev sudo apt-get install python-opencv 如果你还想了解更多,下面提供的那么多链接中你一定找到方法的,饭都送到你面前你不会吃,那么你活该饿着. 平时自己习惯用vim 所以在树莓派上安装了vim编辑器 安装命令  sudo apt-get  install  vim 就可以了. 好了,可以运行一个opencv的例子来检

一招制敌 - 玩转 AngularJS 指令的 Scope (作用域)【转】

学习了AngularJS好长时间,最近再次回首看看指令这部分的时候,觉得比自己刚开始学习的时候理解的更加深入了,尤其是指令的作用域这部分. 当初看的是<AngularJS权威指南>这本书,但是感觉这本书关于这方面讲的不是很细致,另外吐槽一下,这本书中文版印刷的质量不是很好,很多地方都有错误:不过讲的还是可以的,是一本学习AngularJS的好书. 下面我们就来详细分析一下指令的作用域.在这之前希望你对AngularJS的Directive有一定的了解,不然你对下面部分的理解可能会有一点难度.

准备写一个基于go、angularjs的系统管理平台

功能要求: 监控 http.tcp.udp 等服务状态 分析 nginx.tomcat.weblogic 等日志 监控 linux 服务器 cpu.硬盘.内存.网卡流量 邮件报警 go 主要用来写后台代码和监控 linux 服务器的 client 代码,并提供一个 api 给 angularjs 来查询和更新数据. 上面这些功能使用 python 更容易实现一些(python 有许多现成的模块可以使用),只不过拿这个项目作为学习 go 的一个手段.目前完成了一部分的 go 和 angularjs

玩转angularJs——通过自定义ng-model,不仅仅只是input可以有双向绑定

angularJs双向绑定特性在开发中很方便很实用,但是由于ng-model一般只能挂在input上,因此我们需要自定义ng-model来在div等元素上使用该标签. 自定义指令: 1 //自定义ngModel的属性 2 .directive('contenteditable', ['$window', function() { 3 return { 4 restrict: 'A', 5 require: '?ngModel', // 此指令所代替的函数 6 link: function(sco