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 相关文章推荐
关于IE7 IE8弹出窗口顶上
Dec 22 Javascript
function foo的原型与prototype属性解惑
Nov 19 Javascript
JQuery中使用on方法绑定hover事件实例
Dec 09 Javascript
js面向对象之常见创建对象的几种方式(工厂模式、构造函数模式、原型模式)
Nov 09 Javascript
JavaScript中数组slice和splice的对比小结
Sep 22 Javascript
AngularJS入门教程之多视图切换用法示例
Nov 02 Javascript
javascript设计模式之Adapter模式【适配器模式】实现方法示例
Jan 13 Javascript
vue-vuex中使用commit提交mutation来修改state的方法详解
Sep 16 Javascript
Vue 框架之键盘事件、健值修饰符、双向数据绑定
Nov 14 Javascript
js嵌套的数组扁平化:将多维数组变成一维数组以及push()与concat()区别的讲解
Jan 19 Javascript
Vue实现回到顶部和底部动画效果
Jul 31 Javascript
JS document内容及样式操作完整示例
Jan 14 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
Cannot modify header information错误解决方法
2008/10/08 PHP
php数据库密码的找回的步骤
2011/01/12 PHP
php var_export与var_dump 输出的不同
2013/08/09 PHP
PHP文件上传判断file是否己选择上传文件的方法
2014/11/10 PHP
PHP中Session可能会引起并发问题
2015/06/26 PHP
php通过淘宝API查询IP地址归属等信息
2015/12/25 PHP
Js 本页面传值实现代码
2009/05/17 Javascript
javascript 变量作用域 代码分析
2009/06/26 Javascript
JQuery遍历json数组的3种方法
2014/11/08 Javascript
使用jQuery仿苹果官网焦点图特效
2014/12/23 Javascript
浅谈JavaScript字符串与数组
2015/06/03 Javascript
javascript中eval解析JSON字符串
2016/02/27 Javascript
Backbone.js框架中Model与Collection的使用实例
2016/05/07 Javascript
JS使用ActiveXObject实现用户提交表单时屏蔽敏感词功能
2017/06/20 Javascript
JavaScript框架Angular和React深度对比
2017/11/20 Javascript
node.js多个异步过程中判断执行是否完成的解决方案
2017/12/10 Javascript
vue axios请求拦截实例代码
2018/03/29 Javascript
Node.js文件编码格式的转换的方法
2018/04/27 Javascript
jQuery实现获取动态添加的标签对象示例
2018/06/28 jQuery
JavaScript实现无限级递归树的示例代码
2019/03/29 Javascript
小程序使用分包的示例代码
2020/03/23 Javascript
Python UnboundLocalError和NameError错误根源案例解析
2018/10/31 Python
Python操作Excel工作簿的示例代码(\*.xlsx)
2020/03/23 Python
Python列表嵌套常见坑点及解决方案
2020/09/30 Python
浅谈利用缓存来优化HTML5 Canvas程序的性能
2015/05/12 HTML / CSS
C语言变量的命名规则都有哪些
2013/12/27 面试题
.NET面试题:什么是值类型和引用类型
2016/01/12 面试题
非常详细的C#面试题集
2016/07/13 面试题
业务经理岗位职责
2013/11/11 职场文书
计算机专业推荐信范文
2013/11/20 职场文书
表彰先进集体通报
2014/01/12 职场文书
劳动竞赛活动方案
2014/02/20 职场文书
2015年扶贫帮困工作总结
2015/05/20 职场文书
大学自主招生自荐信(2016精选篇)
2016/01/28 职场文书
2016年学校“6﹒26国际禁毒日”宣传活动总结
2016/04/05 职场文书
Redis基本数据类型List常用操作命令
2022/06/01 Redis