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 相关文章推荐
jquery 插件 人性化的消息显示
Jan 21 Javascript
JQuery页面图片切换和新闻列表滚动效果的具体实现
Sep 26 Javascript
多种方法实现JS动态添加事件
Nov 01 Javascript
node.js中的socket.io入门实例
Apr 26 Javascript
基于Bootstrap重置输入框内容按钮插件
May 12 Javascript
微信+angularJS的SPA应用中用router进行页面跳转,jssdk校验失败问题解决
Sep 09 Javascript
关于RequireJS的简单介绍即使用方法
Oct 20 Javascript
利用jquery实现实时更新歌词的方法
Jan 06 Javascript
js浏览器滚动条卷去的高度scrolltop(实例讲解)
Jul 07 Javascript
微信小程序实现添加手机联系人功能示例
Nov 30 Javascript
详解使用 Node.js 开发简单的脚手架工具
Jun 08 Javascript
vue element动态渲染、移除表单并添加验证的实现
Jan 16 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
追忆往昔!浅谈收音机的百年发展历史
2021/03/01 无线电
采用header定义为文件然后readfile下载(隐藏下载地址)
2014/01/31 PHP
Laravel5.4框架使用socialite实现github登录的方法
2019/03/20 PHP
PHP超全局变量实现原理及代码解析
2020/09/01 PHP
jQuery 使用手册(七)
2009/09/23 Javascript
JavaScript 序列化对象实现代码
2009/12/18 Javascript
JavaScript获取页面上某个元素的代码
2011/03/13 Javascript
Jvascript学习实践案例(开发常用)
2012/06/25 Javascript
jQuery中bind,live,delegate与one方法的用法及区别解析
2013/12/30 Javascript
laytpl 精致巧妙的JavaScript模板引擎
2014/08/29 Javascript
使用JavaScript链式编程实现模拟Jquery函数
2014/12/21 Javascript
Hallo.js基于jQuery UI所见即所得的Web编辑器
2016/01/26 Javascript
angularjs 中$apply,$digest,$watch详解
2016/10/13 Javascript
jquery滚动条插件slimScroll使用方法
2017/02/09 Javascript
jquery中each循环的简单回滚操作
2017/05/05 jQuery
ztree简介_动力节点Java学院整理
2017/07/19 Javascript
es6学习之解构时应该注意的点
2017/08/29 Javascript
BACKBONE.JS 简单入门范例
2017/10/17 Javascript
JS动态添加元素及绑定事件造成程序重复执行解决
2017/12/07 Javascript
JS中offset和匀速动画详解
2018/02/06 Javascript
vue中v-text / v-html使用实例代码详解
2019/04/02 Javascript
vue实现循环滚动列表
2020/06/30 Javascript
原生js实现自定义难度的扫雷游戏
2021/01/22 Javascript
采用python实现简单QQ单用户机器人的方法
2014/07/03 Python
Python连接SQLServer2000的方法详解
2017/04/19 Python
Python更新数据库脚本两种方法及对比介绍
2017/07/27 Python
Python编程实现使用线性回归预测数据
2017/12/07 Python
Python通过paramiko远程下载Linux服务器上的文件实例
2018/12/27 Python
Python二维码生成识别实例详解
2019/07/16 Python
详解Python 4.0 预计推出的新功能
2019/07/26 Python
解决pytorch 数据类型报错的问题
2021/03/03 Python
HTML5 Video标签的属性、方法和事件汇总介绍
2015/04/24 HTML / CSS
护士自我鉴定范文
2013/10/06 职场文书
预备党员群众路线思想汇报2014
2014/10/25 职场文书
出差报告范文
2014/11/06 职场文书
2014年体育工作总结
2014/11/24 职场文书