原文链接:a better way to learn AngularJS - promises

AngularJS通过内置的$q服务提供Promise编程模式。通过将异步函数注册到promise对象,Promise编程模式提供一种链式调用异步函数的方式。

Promise模式作为ES6规范之一,取得JavaScript原生支持。AngularJS中$q服务提供的接口,非常近似这一新的规范,所以代码移植到ES6版本将会非常容易。

初始化:

<html>
<head>
<title>Promise fun</title>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0-beta.5/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="app">
</body>
</html>
function getData($timeout, $q) {
return function() {
var defer = $q.defer() // 模拟异步函数
$timeout(function() {
}, 2000) return defer.promise
}
} angular.module('app', [])
.factory('getData', getData)
.run(function(getData) {
var promise = getData();
})

为了简单起见,我们使用$timeout()服务来模拟异步函数。但实际而言,Promose模式最常见的应用场景是运用$http服务的AJAX回调函数。

defer对象

defer对象只是一种暴露promise对象和promise对象相关方法的对象。defer对象通过调用$q.deferred()方法构造出来,而且defer对象暴露了三个主要方法:resolve(),reject(),和notify()。对应的promise对象可以通过.promise属性访问。

我们可以使用defer对象发送“异步函数成功完成”的信号:

function getData($timeout, $q) {
return function() {
var defer = $q.defer() // 模拟异步函数
$timeout(function() {
defer.resolve('data received!')
}, 2000) return defer.promise
}
}

这里,我们先创建了一个新的defer对象,然后返回它的.promise属性。同时,我们执行我们的异步函数,当在该函数完成的时候,我们执行defer对象的resolve()方法。resolve()函数的参数会被传递给接下来的回调函数。

promise对象

现在,我们得到了一个promise对象(通过defer.promise得到),接下来,让我们注册一个回调函数,该回调函数会在异步函数执行完成后被调用。

使用then()方法为promise对象绑定一个回调函数,该回调函数将返回的字符串打印出来:

.run(function(getData) {
var promise = getData()
.then(function(string) {
console.log(string)
})
})

现在,当你刷新页面,两秒后你会看到控制台打印出"data recived!"

reject一个promise对象

注:简而言之,promise的resolve是发出“执行成功”的信号,而reject则是发出“执行失败”的信号。当信号发出,promise会根据不同的信号,执行不同的回调函数。

我们已经知道如何resolve一个promise对象,但如果一个异步函数调用失败了,会怎么样呢?

我们使用Math.random()函数模拟promise对象有50%的机会被reject:

function getData($timeout, $q) {
return function() {
var defer = $q.defer() // 模拟异步函数
$timeout(function() {
if(Math.round(Math.random())) {
defer.resolve('data received!')
} else {
defer.reject('oh no an error! try again')
}
}, 2000)
return defer.promise
}
}

then()方法的第二个参数可以(可选的)接受一个错误处理回调函数,当promise被reject时才会调用该回调函数。

将错误处理函数作为第二参数传给then()

.run(function(getData) {
var promise = getData()
.then(function(string) {
console.log(string)
}, function(error) {
console.error(error)
})
})

现在,如果你再次刷新页面,你有50%的几率能看到错误消息!

通过调用多个不同then()方法,在同一个promise对象上可以注册多个回调函数。这些函数会按照他们注册的顺序一一被调用。

使用$q构造函数

$q服务本身也是一个函数,它能够让你快速的将一个异步回调函数转换成一个基于Promise模式的函数。

将这个模拟异步函数重写成一个使用$q()返回promise对象的函数:

function getData($timeout, $q) {
return function() { // 模拟异步函数
return $q(function(resolve, reject) {
$timeout(function() {
if(Math.round(Math.random())) {
resolve('data received!')
} else {
reject('oh no an error! try again')
}
}, 2000)
}) }
}

这个方法实现的效果跟手动创建defer对象的效果是一样的 -- 你采用哪种方式取决于你的偏好和你是否想要在代码中使用notify()

finally方法

Promise模式保证成功回调函数和错误回调函数中,其中必定有一个会被执行,但两者永远不会同时执行。如果你需要确保不论promise对象的结果如何,都执行某一个特殊的函数,那该怎么办?你可以在promise对象上注册一个finally()方法。对于将代码重置为可知状态下,这方法是非常有帮助的。

使用finally()方法将异步函数完成时的时间戳打印出来:

.run(function(getData) {
var promise = getData()
.then(function(string) {
console.log(string)
}, function(error) {
console.error(error)
})
.finally(function() {
console.log('Finished at:', new Date())
})
})

不管promise对象是被resolve还是被reject,你都能看到控制台打印出当前时间。

Promise链式编程

Promise模式一个非常强大的特性是能够链式编写回调函数。这个特性使数据能够在回调链的每一步上进行传递,处理和改变。虽然这一语法非常容易理解,但有时候这语法也会令人困惑。

让我们先看一个基础例子。

首先,我们对我们的异步函数进行修改,我们给resolve函数传入一个0-9之间的随机数(不再是"data received"字符串):

function getData($timeout, $q) {
return function() {
// 模拟异步函数
return $q(function(resolve, reject) {
$timeout(function() {
resolve(Math.floor(Math.random() * 10))
}, 2000)
})
}
}

当页面刷新,你应该能够看到一个0-9之间的整数被打印出来。

为了链式调用,我们需要修改回调函数,使其能够返回一个值。

修改promise回调函数,使其返回上述随机数乘以2的值:

.run(function(getData) {
var promise = getData()
.then(function(num) {
console.log(num)
return num * 2
})
})

现在,我们可以使用then()函数将另一个回调函数绑定到我们的promise对象上,该函数会在第一个回调函数返回值时被调用。上述随机数两倍的值会传递到第二个回调函数中:

.run(function(getData) {
var promise = getData()
.then(function(num) {
console.log(num)
return num * 2
})
.then(function(num) {
console.log(num) // = random number * 2
})
})

虽然这只是一个简单的例子,但是它阐述了一个非常强大的概念。此外,你不但可以从promise回调函数中返回一个简单的值,你还能够返回一个新的promise对象。那么,promise链会“暂停”直到这个返回的promise对象被resolve。这个特性使你能够链式编写多个异步函数调用(比如多个服务端请求)。

总结

在angularJS框架中,Promise模式编程已经发挥起重要的作用,随着ES6的发布,将来Promise模式在JavaScript中的作用也会越来越重要。虽然它一开始看起来难以理解(尤其是链式调用),但promise模式为解决异步代码提供了一套直观且简洁的接口,也正因如此,Promise成为了现代JavaScript中的一个基础构建模块。

angularJS中的Promise对象($q)的深入理解的更多相关文章

  1. Angularjs中的promise

    promise 是一种用异步方式处理值的方法,promise是对象,代表了一个函数最终可能的返回值或抛出的异常.在与远程对象打交道非常有用,可以把它们看成一个远程对象的代理. 要在Angular中创建 ...

  2. AngularJS 中的Promise --- $q服务详解

    先说说什么是Promise,什么是$q吧.Promise是一种异步处理模式,有很多的实现方式,比如著名的Kris Kwal's Q还有JQuery的Deffered. 什么是Promise 以前了解过 ...

  3. AngularJS 中的 Promise 和 设计模式(转)

    原文地址:http://my.oschina.net/ilivebox/blog/293771 目录[-] Promise 简单例子 链式 Promise Parallel Promises And ...

  4. AngularJS 中的 Promise 和 设计模式

    解决 Javascript 异步事件的传统方式是回调函数:调用一个方法,然后给它一个函数引用,当这个方法完结的时候执行这个函数引用. <!-- lang: js --> $.get('ap ...

  5. es6中的promise对象

    Promise是异步里面的一种解决方案,解决了回调嵌套的问题,es6将其进行了语言标准,同意了用法,提供了`promise`对象, promise对象有三种状态:pending(进行中) .Resol ...

  6. angular学习笔记(二十八-附2)-$http,$resource中的promise对象

    下面这种promise的用法,我从第一篇$http笔记到$resource笔记中,一直都有用到: HttpREST.factory('cardResource',function($resource) ...

  7. 通过一道笔试题浅谈javascript中的promise对象

    因为前几天做了一个promise对象捕获错误的面试题目,所以这几天又重温了一下promise对象.现在借这道题来分享下一些很基础的知识点. 下面是一个面试题目,三个promise对象捕获错误的例子,返 ...

  8. angularJS中XHR与promise

    angularJS应用是完全运行在客户端的应用,我们可以通过angularJS构建一个不需依赖于后端,同时能够实现动态内容和响应的web应用,angularJS提供了将应用与远程服务器的信息集成在一起 ...

  9. AngularJS中处理多个promise

    在使用AngularJS中处理promise的时候,有时会碰到需要处理多个promise的情况. 最简单的处理就是每个promise都then.如下: var app = angular.module ...

随机推荐

  1. SQL学习整理_2

    字符串处理,字符串函数不会改变存储在表中的数据内容,他们只是把函数结果当成查询结果返回. 1. SELECT right(name,2) FROM my_list   --从my_list列表中取出n ...

  2. 去除magento多店铺URL地址中的“___from_store=”

    magento 的多店铺功能,大多数情况下是根据语言来进行选择的,当添加了多店铺之后,一般情况下我们会选择开启添加store code到url地址中. Magento 自带的这种功能算是比较不错了,但 ...

  3. wordpress取文章时间

    wordpress的时间处理, 取文章的本地时间用get_the_time('c'),其中c可以用“D, d M Y H:i:s"等替代. 取文章的UTC时间用get_post_time(' ...

  4. C#开源系统大汇总(个人收藏)

    C#开源系统大汇总 一.AOP框架        Encase 是C#编写开发的为.NET平台提供的AOP框架.Encase 独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖 ...

  5. Python开发程序:学员管理系统(mysql)

    主题:学员管理系统 需求: 用户角色,讲师\学员, 用户登陆后根据角色不同,能做的事情不同,分别如下 讲师视图: 管理班级,可创建班级,根据学员qq号把学员加入班级 可创建指定班级的上课纪录,注意一节 ...

  6. delphi之TDataset

    最近遇到了很多问题,现在做一下总结. 字符串处理: 字符串相加 var S1, S2: String; begin S1 := Concat('A', 'B'); // 连接两个字符串,S1变量等于A ...

  7. 常用的WinAPI函数整理

    常用的WinAPI函数整理 一.进程  创建进程:    CreateProcess("C:\\windows\\notepad.exe",0,0,0,0,0,0,0,&s ...

  8. hadoop实战 -- 网站日志KPI指标分析

    本项目分析apache服务器产生的日志,分析pv.独立ip数和跳出率等指标.其实这些指标在第三方系统中都可以检测到,在生产环境中通常用来分析用户交易等核心数据,此处只是用于演示说明日志数据的分析流程. ...

  9. Oracle 正则表达式函数-REGEXP_SUBSTR 使用例子

    原文在这 戳 REGEXP_SUBSTR 5个参数 第一个是输入的字符串 第二个是正则表达式 第三个是标识从第几个字符开始正则表达式匹配.(默认为1) 第四个是标识第几个匹配组.(默认为1) 第五个是 ...

  10. HDU 4504 威威猫系列故事——篮球梦(dp)

    http://acm.hdu.edu.cn/showproblem.php?pid=4504 题目大意: 中文都看得懂.不过我是看hint才正确理解什么意思的.开始的时候理解错了. 解题思路: 给定时 ...