- Ember.js 指南
- 入门指南 - 介绍
- 入门指南 - 应用规划
- 入门指南 - 创建静态页面
- 入门指南 - 获取Ember.js和相应依赖
- 入门指南 - 添加第一个路由与模板
- 入门指南 - 建立模型
- 入门指南 - 使用静态数据
- 入门指南 - 显示模型数据
- 入门指南 - 显示模型的完成状态
- 入门指南 - 创建新的模型实例
- 入门指南 - 标记模型为完成或未完成
- 入门指南 - 显示未完成待办事项的数量
- 入门指南 - 切换显示和编辑状态
- 入门指南 - 接受修改
- 入门指南 - 删除模型
- 入门指南 - 添加子路由
- 入门指南 - 显示未完成待办事项
- 入门指南 - 显示已完成待办事项
- 入门指南 - 显示所有待办事项
- 入门指南 - 添加移除所有已完成待办事项的按钮
- 入门指南 - 提示所有待办事项已完成
- 入门指南 - 切换已完成和未完成待办事项
- 入门指南 - 更换FixtureAdapter
- 获取 Ember - 获取Ember
- 概念 - 核心概念
- 概念 - 命名惯例
- 对象模型 - 类与实例
- 对象模型 - 计算属性
- 对象模型 - 计算属性和带@each的集合数据
- 对象模型 - 观察器
- 对象模型 - 绑定
- 对象模型 - 重新打开类和实例
- 对象模型 - 绑定,观察器,计算属性:如何选择?
- 应用 - 介绍
- 模板 - 应用模板
- 模板 - Handlebars基础
- 模板 - 条件表达式
- 模板 - 显示项目列表
- 模板 - 切换作用域
- 模板 - 绑定元素属性
- 模板 - 绑定元素类名称
- 模板 - 链接
- 模板 - 操作
- 模板 - 输入助手
- 模板 - 开发助手
- 模板 - 用助手来渲染
- 模板 - 编写助手方法
- 路由 - 介绍
- 路由 - 定义路由
- 路由 - 生成的对象
- 路由 - 指定路由的模型
- 路由 - 设置控制器
- 路由 - 渲染模板
- 路由 - 重定向
- 路由 - 指定地址API
- 路由 - 查询参数
- 路由 - 异步路由
- 路由 - 加载中/错误子状态
- 路由 - 阻止和重试过渡
- 组件 - 介绍
- 组件 - 定义组件
- 组件 - 传递属性
- 组件 - 包裹内容
- 组件 - 自定义组件元素
- 组件 - 使用Action处理用户交互
- 组件 - 从组件发送操作给应用
- 控制器 - 介绍
- 控制器 - 代表单一模型
- 控制器 - 代表多模型
- 控制器 - 管理控制器间的依赖
- 模型 - 介绍
- 模型 - 定义模型
- 模型 - 创建和删除记录
- 模型 - 将记录推入仓库
- 模型 - 持久化记录
- 模型 - 查询记录
- 模型 - 使用记录
- 模型 - 使用Fixture
- 模型 - 连接HTTP服务器
- 模型 - 处理元数据
- 模型 - 自定义适配器
- 模型 - 常见问题
- 视图 - 介绍
- 视图 - 定义视图
- 视图 - 处理事件
- 视图 - 在模板中插入视图
- 视图 - 为视图添加布局
- 视图 - 自定义视图元素
- 视图 - 内置视图
- 视图 - 手动管理视图层级
- 枚举 - 介绍
- 测试 - 介绍
- 测试 - 集成测试
- 测试 - 测试助手
- 测试 - 测试用户交互
- 测试 - 单元测试
- 测试 - 单元测试基础
- 测试 - 测试组件
- 测试 - 测试控制器
- 测试 - 测试路由
- 测试 - 测试模型
- 测试 - 自动化测试
- 配置Ember.js - 禁用基本类型扩展
- 配置Ember.js - 嵌入式应用
- 配置Ember.js - 特性标识
- Cookbook - 简介
- Cookbook - 用户界面与交互
- Cookbook - 事件处理和数据绑定
- Cookbook - 助手与组件
- Cookbook - 使用对象
- 理解Ember.js - 视图层
- 理解Ember.js - 管理异步
- 理解Ember.js - 模板自动更新
- 理解Ember.js - 调试
- 理解Ember.js - 运行循环
路由 - 异步路由
英文原文: http://emberjs.com/guides/routing/asynchronous-routing/
本节内容主要介绍一些路由的高级特性,以及路由是如何处理应用中的一些复杂异步逻辑的。
承诺简介
Ember在路由中处理异步逻辑的方案主要依赖于承诺(Promise)。简单地说,承诺就是代表了最后的值的对象。承诺可以被履行(成功的获得了最后的结果)也可以被拒绝(没有获得最后的结果)。处理获取最后的值,或者承诺被拒绝的方法是通过承诺的then
方法来实现的。该方法接收两个可选的回调函数,一个用于履行承诺时执行,一个则是在拒绝时执行。如果一个承诺被履行,那么最后的值会作为履行处理函数的参数传入;如果一个承诺被拒绝,拒绝的原因会作为拒绝处理函数的参数传入。例如:
1 2 3 4 5 6 7 8 9 10 11 | var promise = fetchTheAnswer(); promise.then(fulfill, reject); function fulfill(answer) { console.log("The answer is " + answer); } function reject(reason) { console.log("Couldn't get the answer! Reason: " + reason); } |
承诺的强大很大程度来源于承诺可以连接在一起,形成一个有序的异步操作链:
1 2 3 4 5 6 7 | // Note: jQuery AJAX methods return promises var usernamesPromise = Ember.$.getJSON('/usernames.json'); usernamesPromise.then(fetchPhotosOfUsers) .then(applyInstagramFilters) .then(uploadTrendyPhotoAlbum) .then(displaySuccessMessage, handleErrors); |
在上述的例子中,如果fetchPhotosOfUsers
、applyInstagramFilters
、或uploadTrendyPhotoAlbum
中任意一个方法返回的承诺被拒绝了,handleErrors
将会被调用,并且告知拒绝的原因。在这种方式下,承诺将异步行为组织为与try-catch
相似的形式,避免了一层层向右嵌套的回调,提供了一种健全有效的管理应用中异步逻辑的方案。
本指南中并不涉及承诺工作的各种场景,如果希望知道更多的关于承诺的介绍,可以查看Ember使用的RSVP的README文件。
路由会为承诺暂停
当在路由间过渡时,Ember的路由通过model
钩子获取所有将会在过渡的最后传递给路由控制器的模型。如果model
钩子(或相关的beforeModel
或afterModel
钩子)返回的是普通的对象或数组(非承诺),那么过渡会立即完成。但是如果model
钩子(包括beforeModel
和afterModel
钩子)返回一个承诺(或者将一个承诺作为参数传递给transitionTo
方法),那么过渡将暂停直到承诺被履行或者拒绝。
如果承诺得到履行,那么过渡将从其停止的地方开始,并着手处理下一个路由(子路由)的模型,如果这个路由依然返回一个承诺,那么又将暂停进行等待,以此类推,知道所有目标路由的模型都被获取到了。传递给每个路由的setupController
钩子的值都是履行完承诺后的值。
一个基本的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 | App.TardyRoute = Ember.Route.extend({ model: function() { return new Ember.RSVP.Promise(function(resolve) { Ember.run.later(function() { resolve({ msg: "Hold Your Horses" }); }, 3000); }); }, setupController: function(controller, model) { console.log(model.msg); // "Hold Your Horses" } }); |
当过渡到TardyRoute
时,路由的model
钩子将被调用,并且返回一个要三秒后才会被履行的承诺,在此期间路由会被暂停在一个中间状态。当承诺最后被履行时,路由将继续过渡并最后将获得的对象传递给TardyRoute
的setupController
钩子。
当在显示一个新模板之前需要确保路由的数据被完全加载成功的情况下,这种暂停在承诺的行为非常有用。
当承诺被拒绝时
之前讲到了一个承诺被履行的情况会是什么样,那么如果承诺被拒绝了又会是怎样呢?
在默认的情况下,如果一个模型承诺在过渡过程中被拒绝了,那么过渡就会被取消,不会有新目标路由的模板被渲染,控制台也会同时记录一条错误。
通过在路由的actions
哈希里,定义一个error
处理器,可以用来处理承诺被拒绝的情况。当一个承诺被拒绝时,路由上会被触发一个error
事件,如果没有自定义的错误处理,那么该事件会一直冒泡直至ApplicationRoute
提供的缺省错误处理器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | App.GoodForNothingRoute = Ember.Route.extend({ model: function() { return Ember.RSVP.reject("FAIL"); }, actions: { error: function(reason) { alert(reason); // "FAIL" // Can transition to another route here, e.g. // this.transitionTo('index'); // Uncomment the line below to bubble this error event: // return true; } } }); |
在上述例子中,错误事件将停留在GoodForNothingRoute
的错误处理器中,而不会往上冒泡。如果需要继续让事件向上冒泡到ApplicationRoute
,那么需要在错误处理器里面返回true
。
从拒绝处恢复
被拒绝的模型承诺会停止过渡,但是由于承诺是可以链式相连的,那么就可以在model
钩子里面捕获到承诺的拒绝,然后将其转换为履行,这样就不会停止过渡了。
1 2 3 4 5 6 7 8 9 | App.FunkyRoute = Ember.Route.extend({ model: function() { return iHopeThisWorks().then(null, function() { // Promise rejected, fulfill with some default value to // use as the route's model and continue on with the transition return { msg: "Recovered from rejected promise" }; }); } }); |
beforeModel
和afterModel
model
钩子包含了基于承诺暂停过渡的许多应用场景,但是有的时候还是需要beforeModel
和afterModel
这两个钩子来提供帮助。最常见的原因是通过{{link-to}}
或者{{transitionTo}}
(最为URL改变导致的过渡的对比)过渡到一个具有动态URL端,这时将过渡到的路由的模型早早就被指定了(例如{{#link-to 'article' article}}
或者this.transitionTo('article', article)
),这种情况下,model
钩子并不会被调用。当路由正在收集所有路由的模型来执行过渡时,就需要使用beforeModel
和afterModel
钩子来处理所有逻辑。
beforeModel
beforeModel
钩子是这两个中更为常用到的一个,beforeModel
在路由器尝试解决给定路由的模型时被调用。或者说,它是在model
钩子被调用前调用的。如果model
钩子不会被调用,那么beforeModel
会在路由器尝试解决传入到路由的模型承诺之前被调用。
跟model
一样,如果beforeModel
返回一个承诺,那么路由将会被暂停至承诺被履行时,或者是在承诺被拒绝时触发一个error
事件。
下面详细的列出了beforeModel
非常有用的几种用例:
- 决定是否在执行一个潜在的在
model
中的费时的服务器端查询之前跳转到其他的路由 - 确定用户在执行
model
之前有一个身份令牌 - 加载路由所需要的应用代码
1 2 3 4 5 6 7 | App.SecretArticlesRoute = Ember.Route.extend({ beforeModel: function() { if (!this.controllerFor('auth').get('isLoggedIn')) { this.transitionTo('login'); } } }); |
afterModel
afterModel
钩子在路由的模型(可能是一个承诺)被解决之后调用,并与model
和beforeModel
一样,遵从基于承诺的暂停原则。afterModel
将传入被解决的模型,并可以在这个已经完全解决了的模型上添加一些附件的逻辑操作。
1 2 3 4 5 6 7 8 9 10 11 12 | App.ArticlesRoute = Ember.Route.extend({ model: function() { // App.Article.find() returns a promise-like object // (it has a `then` method that can be used like a promise) return App.Article.find(); }, afterModel: function(articles) { if (articles.get('length') === 1) { this.transitionTo('article.show', articles.get('firstObject')); } } }); |
这里也许对为什么不将afterModel
的逻辑放入model
承诺履行时要执行的处理器而感到好奇;其实原因很简单,而且之前也提到过,就是当过渡是被如{{link-to}}
或transitionTo
之类的提供了模型给路由的情况时,model
钩子并不会被调用。
更多资源
- Embercasts: 客户端身份验证2
- RC6发布的文章中的描述
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论