angular.js4使用 RxJS 处理多个 Http 请求


Posted in Javascript onSeptember 23, 2017

有时候进入某个页面时,我们需要从多个 API 地址获取数据然后进行显示。管理多个异步数据请求会比较困难,但我们可以借助 Angular Http 服务和 RxJS 库提供的功能来实现上述的功能。处理多个请求有多种方式,使用串行或并行的方式。

基础知识

mergeMap

mergeMap 操作符用于从内部的 Observable 对象中获取值,然后返回给父级流对象。

合并 Observable 对象

const source = Rx.Observable.of('Hello');
//map to inner observable and flatten
const example = source.mergeMap(val => Rx.Observable.of(`${val} World!`));

const subscribe = example.subscribe(val => console.log(val)); //output: 'Hello World!'

在上面示例中包含两种 Observable 类型:

  • 源 Observable 对象 - 即 source 对象
  • 内部 Observable 对象 - 即 Rx.Observable.of(`${val} World!`) 对象

仅当内部的 Observable 对象发出值后,才会合并源 Observable 对象输出的值,并最终输出合并的值。

forkJoin

forkJoin 是 Rx 版本的 Promise.all(),即表示等到所有的 Observable 都完成后,才一次性返回值。

合并多个 Observable 对象

const getPostOne$ = Rx.Observable.timer(1000).mapTo({id: 1});
const getPostTwo$ = Rx.Observable.timer(2000).mapTo({id: 2});

Rx.Observable.forkJoin(getPostOne$, getPostTwo$).subscribe(
 res => console.log(res) // [{id: 1}, {id: 2}]
);

处理 Http 请求

我们先来看一下 Angular Http 服务简单示例。

import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http';

import 'rxjs/add/operator/map';

@Component({
 selector: 'app-root',
 template: `
  <p>HttpModule Demo</p>
 `
})
export class AppComponent implements OnInit {
 constructor(private http: Http) { }

 ngOnInit() {
  this.http.get('https://jsonplaceholder.typicode.com/users')
   .map(res => res.json())
   .subscribe(users => console.log(users));
 }
}

上面示例中,我们通过依赖注入方式注入 http 服务,然后在 ngOnInit() 方法中调用 http 对象的 get() 方法来获取数据。这个例子很简单,它只处理一个请求,接下来我们来看一下如何处理两个请求。

Map 和 Subscribe

有些时候,当我们发送下一个请求时,需要依赖于上一个请求的数据。即我们在需要在上一个请求的回调函数中获取相应数据,然后在发起另一个 HTTP 请求。

import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

@Component({
 selector: 'app-root',
 template: `
  <p>{{username}} Detail Info</p>
  {{user | json}}
 `
})
export class AppComponent implements OnInit {
 constructor(private http: Http) { }

 apiUrl = 'https://jsonplaceholder.typicode.com/users';
 username: string = '';
 user: any;

 ngOnInit() {
  this.http.get(this.apiUrl)
   .map(res => res.json())
   .subscribe(users => {
    let username = users[6].username;
    this.http.get(`${this.apiUrl}?username=${username}`)
     .map(res => res.json())
     .subscribe(
      user => {
       this.username = username;
       this.user = user;
      });
   });
 }
}

在上面示例中,我们先从 https://jsonplaceholder.typicode.com/users 地址获取所有用户的信息,然后再根据指定用户的 username 进一步获取用户的详细信息。虽然功能实现了,但有没有更好的解决方案呢?答案是有的,可以通过 RxJS 库中提供的 mergeMap 操作符来优化上述的流程。

mergeMap

import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';

@Component({
 selector: 'app-root',
 template: `
  <p>{{username}} Detail Info</p>
  {{user | json}}
 `
})
export class AppComponent implements OnInit {
 constructor(private http: Http) { }

 apiUrl = 'https://jsonplaceholder.typicode.com/users';

 username: string = '';

 user: any;

 ngOnInit() {
  this.http.get(this.apiUrl)
   .map(res => res.json())
   .mergeMap(users => {
    this.username = users[6].username;
    return this.http.get(`${this.apiUrl}?username=${this.username}`)
     .map(res => res.json())
   })
   .subscribe(user => this.user = user);
 }
}

在上面示例中,我们通过 mergeMap 操作符,解决了嵌套订阅的问题。最后我们来看一下如何处理多个并行的 Http 请求。

forkJoin

接下来的示例,我们将使用 forkJoin 操作符。如果你熟悉 Promises 的话,该操作符与 Promise.all() 实现的功能类似。forkJoin 操作符接收一个 Observable 对象列表,然后并行地执行它们。一旦列表的 Observable 对象都发出值后,forkJoin 操作符返回的 Observable 对象会发出新的值,即包含所有 Observable 对象输出值的列表。具体示例如下:

import { Component, OnInit } from '@angular/core';
import { Http } from '@angular/http';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/forkJoin';

@Component({
 selector: 'app-root',
 template: `
  <p>Post Detail Info</p>
  <ul>
   <li>{{post1 | json}}</li>
   <li>{{post2 | json}}</li>
  </ul>
 `
})
export class AppComponent implements OnInit {
 constructor(private http: Http) { }

 apiUrl = 'https://jsonplaceholder.typicode.com/posts';

 post1: any;

 post2: any;

 ngOnInit() {
  let post1 = this.http.get(`${this.apiUrl}/1`);
  let post2 = this.http.get(`${this.apiUrl}/2`);

  Observable.forkJoin([post1, post2])
   .subscribe(results => {
    this.post1 = results[0];
    this.post2 = results[1];
   });
 }
}

我有话说

除了 mergeMap 外,RxJS 中的 switchMap 有什么用?

switchMap 操作符用于对源 Observable 对象发出的值,做映射处理。若有新的 Observable 对象出现,会在新的 Observable 对象发出新值后,退订前一个未处理完的 Observable 对象。

使用示例:

var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source.switchMap(e => Rx.Observable.interval(100).take(3));

example.subscribe({
  next: (value) => { console.log(value); },
  error: (err) => { console.log('Error: ' + err); },
  complete: () => { console.log('complete'); }
});

示例 marble 图:

source : -----------c--c-----------------...
    concatMap(c => Rx.Observable.interval(100).take(3))
example: -------------0--0-1-2-----------...

以上代码运行后,控制台的输出结果:

0
0
1
2

而在实际使用 Http 服务的场景中,比如实现 AutoComplete 功能,我们可以利用 switchMap 操作符,来取消无用的 Http 请求。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript 八进制转义字符(8进制)
Apr 08 Javascript
教你如何使用firebug调试功能了解javascript闭包和this
Mar 04 Javascript
微信开发 使用picker封装省市区三级联动模板
Oct 28 Javascript
网站申请不到支付宝接口、微信接口,免接口收款实现方式几种解决办法
Dec 14 Javascript
jQuery+CSS3实现点赞功能
Mar 13 Javascript
Vue2.0中集成UEditor富文本编辑器的方法
Mar 03 Javascript
Vue CLI 3搭建vue+vuex最全分析(推荐)
Sep 27 Javascript
JavaScript常见继承模式实例小结
Jan 11 Javascript
JS实现查找数组中对象的属性值是否存在示例
May 24 Javascript
ES6 Promise对象概念及用法实例详解
Oct 15 Javascript
Vue element-ui父组件控制子组件的表单校验操作
Jul 17 Javascript
基于ajax实现上传图片代码示例解析
Dec 03 Javascript
详细介绍RxJS在Angular中的应用
Sep 23 #Javascript
Javascript刷新页面的实例
Sep 23 #Javascript
react-native-fs实现文件下载、文本存储的示例代码
Sep 22 #Javascript
jQuery实现table中两列CheckBox只能选中一个的示例
Sep 22 #jQuery
解决html-jquery/js引用外部图片时遇到看不了或出现403的问题
Sep 22 #jQuery
layer子层给父层页面元素赋值,以达到向父层页面传值的效果实例
Sep 22 #Javascript
js表单序列化判断空值的实例
Sep 22 #Javascript
You might like
PHP加密解密类实例分析
2015/04/20 PHP
thinkPHP统计排行与分页显示功能示例
2016/12/02 PHP
Yii框架表单提交验证功能分析
2017/01/07 PHP
360搜索引擎自动收录php改写方案
2018/04/28 PHP
页面中iframe相互传值传参
2009/12/13 Javascript
jQuery学习笔记 操作jQuery对象 文档处理
2012/09/19 Javascript
让元素在网页中可拖动示例代码
2013/08/13 Javascript
js实现键盘操作实现div的移动或改变的原理及代码
2014/06/23 Javascript
js实现带按钮的上下滚动效果
2015/05/12 Javascript
javascript获取文档坐标和视口坐标
2015/05/26 Javascript
jQuery插件Validate实现自定义表单验证
2016/01/18 Javascript
Javascript中常用的检测方法小结
2016/10/08 Javascript
jquery对所有input type=text的控件赋值实现方法
2016/12/02 Javascript
Bootstrap的popover(弹出框)在append后弹不出(失效)
2017/02/27 Javascript
Linux使用Node.js建立访问静态网页的服务实例详解
2017/03/21 Javascript
微信小程序实现倒计时60s获取验证码
2020/04/17 Javascript
使用vue制作FullPage页面滚动效果
2017/08/21 Javascript
vue+iview写个弹框的示例代码
2017/12/05 Javascript
jquery根据name取得select选中的值实例(超简单)
2018/01/25 jQuery
Bootstrap 模态框自定义点击和关闭事件详解
2018/08/10 Javascript
Bootstrap Table列宽拖动的方法
2018/08/15 Javascript
vue开发拖拽进度条滑动组件
2019/09/21 Javascript
vue-resourc发起异步请求的方法
2020/02/11 Javascript
[06:53]2018DOTA2国际邀请赛寻真——为复仇而来的Newbee
2018/08/15 DOTA
python爬虫实现POST request payload形式的请求
2020/04/30 Python
使用django自带的user做外键的方法
2020/11/30 Python
深入理解Python变量的数据类型和存储
2021/02/01 Python
借助HTML5 Canvas API制作一个简单的猜字游戏
2016/03/25 HTML / CSS
HTML最新标准HTML5总结(必看)
2016/06/13 HTML / CSS
我的珠宝盒:Ma boîte à bijoux
2019/08/27 全球购物
越南母婴用品购物网站:Kids Plaza
2020/04/09 全球购物
环境科学专业个人求职的自我评价
2013/11/28 职场文书
民事诉讼代理授权委托书
2014/10/11 职场文书
员工升职自荐信
2015/03/27 职场文书
2015年银行工作总结范文
2015/04/01 职场文书
超市采购员岗位职责
2015/04/07 职场文书