Ember.js 入门指南——异步路由

本文将为你介绍路由的高级特性,这些高级特性可以用于处理项目复杂的异步逻辑。

关于单词promises,直译是承诺,但是个人觉得还是使用原文吧。读起来顺畅点。

1,promises(承诺)

Ember的路由处理异步逻辑的方式是使用promises。简而言之,promises就是一个表示最终结果的对象。这个对象可能是fulfill(成功获取最终结果)也可能是reject(获取结果失败)。为了获取这个最终值,或者是处理promises失败的情况都可以使用then方法,这个方法接受两个可选的回调方法,一个是promises获取结果成功时执行,一个是promises获取结果失败时执行。如果promises获取结果成功那么获取到的结果将作为成功时执行的回调方法的参数。相反的,如果promises获取结果失败,那么最终结果(失败的原因)将作为promises失败时执行的回调方法的参数。比如下面的代码段,当promises获取结果成功时执行fulfill回调,否则执行reject回调方法。

//  app/routes/promises.js
 
import Ember from ‘ember‘;
 
export default Ember.Route.extend({
       beforeModel: function() {
              console.log(‘execute model()‘);
 
              var promise = this.fetchTheAnswer();
              promise.then(this.fulfill, this.reject);
       },
 
       //  promises获取结果成功时执行
       fulfill: function(answer) {
         console.log("The answer is " + answer);
       },
 
       //  promises获取结果失败时执行
       reject: function(reason) {
         console.log("Couldn‘t get the answer! Reason: " + reason);
       },
 
       fetchTheAnswer: function() {
              return new Promise(function(fulfill, reject){
                     return fulfill(‘success‘);  //如果返回的是fulfill则表示promises执行成功
                     //return reject(‘failure‘);  //如果返回的是reject则表示promises执行失败
              });
       }
});

上述这段代码就是promises的一个简单例子,promises的then方法会根据promises的获取到的最终结果执行不同的回调,如果promises获取结果成功则执行fulfill回调,否则执行reject回调。

promises的强大之处不仅仅如此,promises还可以以链的形式执行多个then方法,每个then方法都会根据promises的结果执行fulfill或者reject回调。

//  app/routes/promises.js
 
import Ember from ‘ember‘;
 
export default Ember.Route.extend({
 
       beforeModel() {
              // 注意Jquery的Ajax方法返回的也是promises
              var promiese = Ember.$.getJSON(‘https://api.github.com/repos/emberjs/ember.js/pulls‘);
              promiese.then(this.fetchPhotoOfUsers)
                            .then(this.applyInstagramFilters)
                            .then(this.uploadThrendyPhotAlbum)
                            .then(this.displaySuccessMessage, this.handleErrors);
 
       },
       fetchPhotoOfUsers: function(){
              console.log(‘fetchPhotoOfUsers‘);
       },
       applyInstagramFilters: function() {
              console.log(‘applyInstagramFilters‘);
       },
       uploadThrendyPhotAlbum: function() {
              console.log(‘uploadThrendyPhotAlbum‘);
       },
       displaySuccessMessage: function() {
              console.log(‘displaySuccessMessage‘);
       },
       handleErrors: function() {
              console.log(‘handleErrors‘);
       }
});

这种情况下会打印什么结果呢??

在前的文章已经使用过Ember.$.getJSON(‘https://api.github.com/repos/emberjs/ember.js/pulls‘);获取数据,是可以成功获取数据的。所以promises获取结果成功,应该执行的是获取成功对应的回调方法。浏览器控制台打印结果如下:

fetchPhotoOfUsers
applyInstagramFilters
uploadThrendyPhotAlbum
displaySuccessMessage

但是如果我把Ember.$.getJSON(‘https://api.github.com/repos/emberjs/ember.js/pulls‘);改成一个不存在的URL,比如改成Ember.$.getJSON(‘https://www.my-example.com‘);执行代码之后控制台会提示出404错误,并且打印‘handleErrors‘。说明promises获取结果失败,执行了then里的reject回调。为了验证每个回调的reject方法再修改修改代码,如下:

//  app/routes/promises.js
 
import Ember from ‘ember‘;
 
export default Ember.Route.extend({
 
       beforeModel() {
              // 注意Jquery的Ajax方法返回的也是promises
              var promiese = Ember.$.getJSON(‘ https://www.my-example.com ‘);
              promiese.then(this.fetchPhotoOfUsers, this.fetchPhotoOfUsersError)
                            .then(this.applyInstagramFilters, this.applyInstagramFiltersError)
                            .then(this.uploadThrendyPhotAlbum, this.uploadThrendyPhotAlbumError)
                            .then(this.displaySuccessMessage, this.handleErrors);
 
       },
       fetchPhotoOfUsers: function(){
              console.log(‘fetchPhotoOfUsers‘);
       },
       fetchPhotoOfUsersError: function() {
              console.log(‘fetchPhotoOfUsersError‘);
       },
       applyInstagramFilters: function() {
              console.log(‘applyInstagramFilters‘);
       },
       applyInstagramFiltersError: function() {
              console.log(‘applyInstagramFiltersError‘);
       },
       uploadThrendyPhotAlbum: function() {
              console.log(‘uploadThrendyPhotAlbum‘);
       },
       uploadThrendyPhotAlbumError: function() {
              console.log(‘uploadThrendyPhotAlbumError‘);
       },
       displaySuccessMessage: function() {
              console.log(‘displaySuccessMessage‘);
       },
       handleErrors: function() {
              console.log(‘handleErrors‘);
       }
});

由于promises获取结果失败故执行其对应的失败处理回调。这种调用方式有点类似于try……catch……,但是本文的重点不是讲解promises,更多有关promises的教材请读者自行Google或者百度吧,在这里介绍一个js库RSVP.js,它可以让你更加简单的组织你的promises代码。

在附上几个promises的参考网站:

1,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

2,http://liubin.github.io/promises-book/

3,http://www.zhangxinxu.com/wordpress/2014/02/es6-javascript-promise-%E6%84%9F%E6%80%A7%E8%AE%A4%E7%9F%A5/

极力推荐看第二个网站的教材,这个网站可以直接运行js代码。还有源码和PDF。非常棒!!!

2,promises中止路由

当发生路由切换的时候,在model回调(或者是beforeMode、afterModel)中获取的数据集会在切换完成的时候传递到路由对应的controller上。如果model回调返回的是一个普通的对象(非promises对象)或者是数组,路由的切换会立即执行,但是如果model回调返回的是一个promises对象,路由的切换将会被中止直到promises执行完成(返回fulfill或者是reject)才切换。

       路由器任务任何一个包含了then方法的对象都是一个promises

如果promises获取结果成功则会从被中止的地方继续往下执行或者是执行路由链的下一个路由,如果promises返回的依然是一个promises,那么路由依然再次被中止,等待promises的返回结果,如果是fulfill则从被中止的地方开始往下执行,以此类推,一直到获取到model回调所需的结果。

传递到每个路由的setupController回调的值都是promises返回fulfill时的值。如下代码:

//  app/routes/tardy.js
 
import Ember from ‘ember‘;
 
export default Ember.Route.extend({
       model: function() {
              return new Ember.RSVP.Promise(function(resolver) {
                     console.log(‘start......‘);
                     Ember.run.later(function() {
                            resolver({ msg: ‘Hold your horses!!‘});
                     }, 3000);
              });
       },
       setupController(controller, model) {
              console.log(‘msg = ‘ + model.msg);
       }
});

一进入路由tardy,model回调就会被执行并且返回一个延迟3秒才执行的promises,在这期间路由会中止。当promises返回fulfill路由会继续执行,并将model返回的对象传递到setupController方法中。

虽然这种中止的行为会影响响应速度但是这是非常必要的,特别是你需要保证model回调得到的数据是完整的数据的时候。

3,promises获取结果失败

文章前面主要讲的是promises获取结果成功的情况,但是如果是获取结果失败的情况又是怎么处理呢??

默认情况下,如果model回调返回的是一个promises对象并且此promises返回的是reject,此时路由切换将被终止,也不会渲染对应的模板,并且会在浏览器控制台打印出错误日志信息,例子promises-ret-reject.js会演示。

你可以自定义处理出错信息的逻辑,只要在route的actions哈希对象中配置即可。当promises获取结果失败的默认情况下会执行一个名为error的处理事件,否则会执行你自定义的处理事件。

//  app/routes/promises-ret-reject.js
 
import Ember from ‘ember‘;
 
export default Ember.Route.extend({
       model: function() {
              //  为了测试效果直接返回reject
              return Ember.RSVP.reject(‘FAIL‘);
       },
       actions: {
              error: function(reason) {
                     console.log(‘reason = ‘ + reason);
 
                     //  如果你想让这个事件冒泡到顶级路由application只需要返回true
                     //  return true;
              }
       }
});

如果没有不允许事件冒泡打印结果仅仅是“reason = FAIL”。并且页面上什么都不显示(不渲染模板)。

如果去掉最后一行代码的注释,让事件冒泡到顶级路由application中的默认方法处理,那么结果又是什么呢?

结果是先打印了处理结果,然后再打印出提示错误的日志信息。并且页面上什么都不显示(不渲染模板)。

4,恢复promises的reject状态

在前面第3点介绍了promises获取结果失败时会终止路由切换,但是如果model返回是一个promises链呢?程序能到就这样死了!!!显然是不行的,做法是把model回调中返回的reject转换为fulfill。这样就可以继续执行或者切换到下一个路由了!

//  app/routes/funky.js
 
import Ember from ‘ember‘;
 
export default Ember.Route.extend({
       model: function() {
              var promises = Ember.RSVP.reject(‘FAIL‘);
              //  由于已经知道promises返回的是reject,所以fulfill回调直接写为null
              return promises.then(null, function() {
                     return { msg: ‘恢复reject状态:其实就是在reject回调中继续执行fulfill状态下的代码。‘ };
              });
       }
});

为了验证model回调的结果,直接在模板上显示msg。

<!--  app/templates/funky.hbs  -->
 
funky模板
<br>
{{model.msg}}

执行URL:http://localhost:4200/funky,得到如下结果:

说明model回调进入到reject回调中,并正确返回了预期结果。

到本文为止有关路由这以整章的内容也全部介绍完毕了!!难点在《Ember.js 入门指南——查询参数》这一篇。能力有限没有把这篇的内容讲明白,暂时搁下待日后完善!

总的来说路由主要职责是获取数据,根据逻辑处理数据。有点MVC架构的dao层,专门做数据的CRUD操作。当然另外一个重要职责就是路由的切换,以及切换的时候参数的设置问题。

结束完这一章下一章接着介绍组件(Component),正好是国庆休息几天再继续吧!!祝大伙国庆快乐!!!

时间: 2024-09-28 21:28:24

Ember.js 入门指南——异步路由的相关文章

Ember.js 入门指南——总目录

Ember.js 是什么?我想对于想学习它的人应该知道它是个什么东西,如果你想了解那就赶紧去 Google 或者百度,本系列教程是通过学习官网教程然后摘抄个人觉得比较重要的部分,加上学习实例整合而成,如有疏漏欢迎提出修改意见,一起成长! Ember官网:http://emberjs.com/ 教程官网:http://guides.emberjs.com/v2.0.0/ 在此简单介绍下 Ember: Ember是一个雄心勃勃的Web应用程序,消除了样板,并提供了一个标准的应用程序架构的JavaSc

Ember.js 入门指南——路由简介

从本文开始,将为大家介绍路由(route),如果你看过前面的<Ember.js 入门指南--{{link-to}} 助手>这篇文章应该初步了解了route.不过在这篇文章中只是简单介绍了路由是定义.路由层次,更深入的route将从本文开始逐一介绍. 当用户使用你的应用时,应用要在不同的状态之间切换.Ember提供了很多工具用于管理那些因应用规模改变而改变的状态. 讲route前先了解URL,在应用中大概会会有如下方式设置URL: 用户第一次加载应用的时: 用户手动改变URL,比如点击按钮之后跳

Ember.js 入门指南——路由定义

当你的应用启动的时候,路由器就会匹配当前的URL到你定义的路由上.然后按照定义的路由层次逐个加载数据.设置应用程序状态.渲染路由对应的模板. 1,基本路由 在app/router.js的map方法里定义的路由会映射到当前的URL.当map方法被调用的时候方法体内的route方法就会创建路由. 下面使用Ember CLI命令创建两个路由: ember generate route about ember generate route favorites 命令执行完之后你可在你的项目目录app/ro

Ember.js 入门指南——model简介2

本文接上一篇<Ember.js 入门指南--model简介1>. 2,核心概念 声明:下面简介内摘抄至http://www.emberjs.cn/guides/models/#toc_. 1,store store是应用存放记录的中心仓库.你可以认为store是应用的所有数据的缓存.应用的控制器和路由都可以访问这个共享的store:当它们需要显示或者修改一个记录时,首先就需要访问store. DS.Store的实例会被自动创建,并且该实例被应用中所有的对象所共享. store可以看做是一个缓存

Ember.js 入门指南——控制器(controller)

ember new chapter5_controllers cd chapter5_controllers ember server 从本篇开始进入第五章控制器,controller在Ember2.0开始越来越精简了,职责也更加单一--处理逻辑. 下面是准备工作. 从新创建一个Ember项目,仍旧使用的是Ember CLI命令创建. 在浏览器执行项目,看到如下信息说明项目搭建成功. Welcome to Ember 1,控制器简介 控制器与组件非常相似,由此,在未来的新版本中很有可能组件将会完

Ember.js 入门指南--目录

本系列文章全部从(http://ibeginner.sinaapp.com/)迁移过来,欢迎访问原网站. Ember.js 是什么?我想对于想学习它的人应该知道它是个什么东西,如果你想了解那就赶紧去 Google 或者百度,本系列教程是通过学习官网教程然后摘抄个人觉得比较重要的部分,加上学习实例整合而成,如有疏漏欢迎提出修改意见,一起成长! Ember官网:http://emberjs.com/ 教程官网:http://guides.emberjs.com/v2.0.0/ 在此简单介绍下 Emb

Ember.js 入门指南——模板渲染

路由的另一个重要职责是渲染同名字的模板. 比如下面的路由设置,posts路由渲染模板posts.hbs,路由new渲染模板posts/new.hbs. Router.map(function() {      this.route('posts', function() {      this.route('new');   }); }); 每一个模板都会渲染到父模板的{{outlet}}上.比如上面的路由设置模板posts.hbs会渲染到模板application.hbs的{{outlet}}

Ember.js 入门指南——属性传递

1,传递参数到组件上 每个组件都是相对独立的,因此任何组件所需的数据都需要通过组件的属性把数据传递到组件中. 比如上篇<Ember.js 入门指南--组件定义>的第三点"{{component item.pn post=item}}"就是通过属性post把数据传递到组件foo-component或者bar-component上.如果在index.hbs中是如下方式调用组件那么渲染之后的页面是空的. {{component item.pn}} 请读者自己修改index.hbs

Ember.js 入门指南——包裹内容

准备工作: ember g route wrapping-content-in-component-route        ember g component wrapping-content-in-component 有些情况下,你需要定义一个包裹其他模板提供的数据的组件.比如下面的例子: <!--  app/templates/components/wrapping-content-in-component.hbs  -->   <h1>{{title}}</h1>