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 工具库 Cloudgamer JavaScript Library v0.1 发布
Oct 29 Javascript
Jquery Validation插件防止重复提交表单的解决方法
Mar 05 Javascript
扩展jquery实现客户端表格的分页、排序功能代码
Mar 16 Javascript
js 剪切板的用法(clipboardData.setData)与js match函数介绍
Nov 19 Javascript
jQuery数据缓存用法分析
Feb 20 Javascript
js生成随机数的过程解析
Nov 24 Javascript
JavaScript常用正则验证函数实例小结【年龄,数字,Email,手机,URL,日期等】
Jan 23 Javascript
js使用swiper实现层叠轮播效果实例代码
Dec 12 Javascript
JS正则表达式封装与使用操作示例
May 15 Javascript
详解小程序云开发数据库
May 20 Javascript
Vue 列表上下过渡效果的实例代码
Jun 25 Javascript
Vue vm.$attrs使用场景详解
Mar 08 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新手上路(七)
2006/10/09 PHP
PHP获取MSN好友列表类的实现代码
2013/06/23 PHP
WordPress中获取指定分类及其子分类下的文章数目
2015/12/31 PHP
php文件上传、下载和删除示例
2020/08/28 PHP
PHP抽象类与接口的区别详解
2019/03/21 PHP
Yii框架布局文件的动态切换操作示例
2019/11/11 PHP
php的无刷新操作实现方法分析
2020/02/28 PHP
jQuery焦点图切换简易插件制作过程全纪录
2014/08/27 Javascript
在JavaScript中处理时间之getHours()方法的使用
2015/06/10 Javascript
纯javascript响应式树形菜单效果
2015/11/10 Javascript
JavaScript简单生成 N~M 之间随机数的方法
2017/01/13 Javascript
jQuery、layer实现弹出层的打开、关闭功能
2017/06/28 jQuery
详解Node.js利用node-git-server快速搭建git服务器
2017/09/27 Javascript
Vue打包后出现一些map文件的解决方法
2018/02/13 Javascript
jquery实现搜索框功能实例详解
2018/07/23 jQuery
小谈angular ng deploy的实现
2020/04/07 Javascript
Python struct模块解析
2014/06/12 Python
实例Python处理XML文件的方法
2015/08/31 Python
opencv转换颜色空间更改图片背景
2019/08/20 Python
Python参数传递对象的引用原理解析
2020/05/22 Python
python 偷懒技巧——使用 keyboard 录制键盘事件
2020/09/21 Python
CSS3制作气泡对话框的实例教程
2016/05/10 HTML / CSS
伦敦高达60%折扣的钻石珠宝商:Purely Diamonds
2018/06/24 全球购物
英国户外服装、鞋类和设备的领先零售商:Millets
2020/10/12 全球购物
销售顾问的岗位职责
2013/11/13 职场文书
后勤主管工作职责
2013/12/07 职场文书
周年庆促销方案
2014/03/15 职场文书
纪念九一八事变演讲稿:牢记历史,捍卫主权
2014/09/14 职场文书
离婚协议书怎么写(范本参考)
2014/09/30 职场文书
老公给老婆的检讨书(精华篇)
2014/10/18 职场文书
交警失职检讨书
2015/01/26 职场文书
求职意向书范本
2015/05/11 职场文书
立案决定书范文
2015/06/24 职场文书
2015年国庆节演讲稿范文
2015/07/30 职场文书
调解协议书范本
2016/03/21 职场文书
面试官问我Mysql的存储引擎了解多少
2022/08/05 MySQL