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 相关文章推荐
弹出广告特效(一个IP只弹出一次)的代码
Jul 27 Javascript
JQuery之拖拽插件实现代码
Apr 14 Javascript
js 获取浏览器版本以此来调整CSS的样式
Jun 03 Javascript
jquery 插件实现瀑布流图片展示实例
Apr 03 Javascript
js验证真实姓名与身份证号是否匹配
Oct 13 Javascript
jQuery实现移动端滑块拖动选择数字效果
Dec 24 Javascript
jquery trigger函数执行两次的解决方法
Feb 29 Javascript
JS实现简单的tab切换选项卡效果
Sep 21 Javascript
详解ElementUI之表单验证、数据绑定、路由跳转
Jun 21 Javascript
浅谈Vue父子组件和非父子组件传值问题
Aug 22 Javascript
Vue解析带html标签的字符串为dom的实例
Nov 13 Javascript
js实现滑动进度条效果
Aug 21 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
调整优化您的LAMP应用程序的5种简单方法
2011/06/26 PHP
php 下载保存文件保存到本地的两种实现方法
2013/08/12 PHP
PHP实现的限制IP投票程序IP来源分析
2016/05/04 PHP
基于CI框架的微信网页授权库示例
2016/11/25 PHP
php获取'/'传参的值简单方法
2017/07/13 PHP
PHP 命名空间和自动加载原理与用法实例分析
2020/04/29 PHP
javascript 定义新对象方法
2010/02/20 Javascript
Angular中实现树形结构视图实例代码
2017/05/05 Javascript
JavaScript创建对象方法实例小结
2018/09/03 Javascript
微信小程序自定义toast的实现代码
2018/11/16 Javascript
vue 实现v-for循环回来的数据动态绑定id
2019/11/07 Javascript
vue项目中使用多选框的实例代码
2020/07/22 Javascript
JavaScript实现图片合成下载的示例
2020/11/19 Javascript
[00:23]DOTA2群星共贺开放测试 25日无码时代来袭
2013/09/23 DOTA
[02:12]打造更好的电竞完美世界:完美盛典回顾篇
2018/12/19 DOTA
使用Python脚本和ADB命令实现卸载App
2017/02/10 Python
Python字典操作详细介绍及字典内建方法分享
2018/01/04 Python
Numpy中矩阵matrix读取一列的方法及数组和矩阵的相互转换实例
2018/07/02 Python
python修改txt文件中的某一项方法
2018/12/29 Python
Python中输入和输出(打印)数据实例方法
2019/10/13 Python
HTML5利用约束验证API来检查表单的输入数据的代码实例
2016/12/20 HTML / CSS
贝玲妃美国官方网站:Benefit美国
2016/08/28 全球购物
CheapTickets香港机票预订网站:CheapTickets.hk
2019/06/26 全球购物
介绍一下Linux中的链接
2016/05/28 面试题
参观接待方案
2014/03/17 职场文书
珍惜资源保护环境的建议书
2014/05/14 职场文书
红色故事演讲稿
2014/05/22 职场文书
设计专业自荐信
2014/06/19 职场文书
2014教师教育实践活动对照检查材料思想汇报
2014/09/21 职场文书
部队个人年终总结
2015/03/02 职场文书
2015年化验员工作总结
2015/04/10 职场文书
幼儿园教师安全责任书
2015/05/08 职场文书
2015年音乐教师个人工作总结
2015/05/20 职场文书
高三生物教学反思
2016/02/22 职场文书
python DataFrame中stack()方法、unstack()方法和pivot()方法浅析
2022/04/06 Python
Shell中的单中括号和双中括号的用法详解
2022/12/24 Servers