かかってこいよ NakamuraEmi
  1. 1 かかってこいよ NakamuraEmi
  2. 2 Life Will Change Lyn
  3. 3 Flower Of Life 发热巫女
  4. 4 Time Bomb Veela
  5. 5 The Night We Stood Lyn
  6. 6 One Last You Jen Bird
  7. 7 Hypocrite Nush
  8. 8 Last Surprise Lyn
  9. 9 Quiet Storm Lyn
  10. 10 Warcry mpi
  11. 11 Libertus Chen-U
2018-04-15 17:45:03

RxJS操作符——数据流的映射

之前我们谈到了类似数组方法的filter的过滤处理,是将符合需求的数据筛出的操作。同理,现在谈到的数据流映射也和数组方法的map函数类似,这类操作不对数据进行筛选,而是对数据流中的所有数据执行一个规定的映射函数将其变为我们需要的数据流。


时间型映射

该类映射根据一定条件改变数据发射的时机,但不改变原始数据。

delay

该方法接受参数为单位为毫秒数的数字或者日期类型,会将数据流做一个时间延迟的映射后再发射。例子如下:

let source = Rx.Observable.timer(0, 1000);
let target = source.delay(1000);

// ---0---1---2---……
// delay
// -------0---1---2---……

delayWhen

同样是对数据流进行延时映射,不同的是每个数据项的延时时间由传入的一个返回数据流的映射函数决定。该函数将源数据值当作参数,仅当映射后的数据流发出值时,源值才会发出。

let source = Rx.Observable.of(1, 2, 1);
let target = source.delayWhen(x => Rx.Observable.timer(1000 * x));

// 1-2-1-|
// delayWhen
// ---1----21-|

附加型映射

该类映射不丢失原始数据,而是在其上附加一些新的功能。

timeInterval

该方法不改变数据流的发射时间,只是将数据流流出的数据映射为一个对象,该对象有两个键,value的值为原值,interval值代表与前一个元素的时间间隔。当元素为第一个元素时代表发射离订阅开始时的时间间隔。

let source = Rx.Observable.timer(0, 1000);
let target = source.timeInterval();
// ---0---1---2---……
// timerInterval
// ---{value: 0,interval: 1000}---{value: 1, interval: 1000}---{value: 2, interval: 1000}---……

timeout

映射数据流和原数据流相同,但是会附加一个超时报错机制。每当源数据生成一个元素就开始计时,如果该时间段内还没有生成下一个元素就报错。该方法接受两个参数,第一个指报错时间,第二个参数可选,用来声明抛出的错误。

let source = Rx.Observable.timer(1000);
let target = source.timeout(500, new Error("timeout!"));

// ---0---1---2---……
// timeout
// ---0--X

timestamp

不改变数据流的发射时间,只是将数据流流出的数据映射为对象,value为原值,timestamp代表发射该值的时间戳。

do

该方法不修改数据流内的数据,只是每次发送时附加一个参数为发射值的回调函数,通常用来调试或执行一些副作用操作。

let clicks = Rx.Observable.fromEvent(document, 'click');
let positions = clicks.do(ev => console.log(ev)).map(ev => ev.clientX);
// 把每次点击映射为X坐标同时打印出该点击事件对象

条件型映射

该类映射通常会有一个断言函数,通过一定条件来决定数据流的映射方法。一般都会改变原有数据。

Materalize/Dematerialize

该类方法用于实现流出的数据值和Notification对象间的转换。该对象包装了来自源数据流的元数据。materalize是包装,dematerialize是拆分。

let source = Rx.Observable.of('a', 'b', 1);
let upperCase = source.map(x => x.toUpperCase());
let materialized = upperCase.materialize();

// ---a---b---1--|
// map materialize
// ---{kind: 'N', value: 'A', error: undefined, hasValue: true}
// ---{kind: 'N', value: 'B', error: undefined, hasValue: true}
// ---{kind: 'E', value: undefined, error: ..., hasValue: false}

map/mapTo

该方法和数组的map方法类似,参数为一个变换函数,作用于源数据流中的每个元素,返回值则为新数据流的对应元素。mapTo则为map的简化版,只接受常量,即把每一个原值都映射为同一输出值。

let source = Rx.Observable.of(1, 2, 3);
let target = source.map(item => item * 2);
// 1-2-3-|
// map
// 2-4-6-|

pluck

如果原数据流的数据值为对象,则可以使用该方法将每个对象值映射为该值的某个具体属性。可以理解为对map方法的特定对象简化。

let source = Rx.Observable.of({name: 'a', age:  1}, {name: 'b', age: 2});
let target = source.pluck('name');
// --{name: 'a', age: 1}--{name: 'b', age: 2}--|
// pluck
// --'a'--'b'--|

scan

该方法类似于数组的reduce函数,第一个参数为累加器函数,对每个原数据使用特定的累加器函数并返回累加值,可以提供一个初始值作为第二个参数。如果没有提供,原数据的第一项会作为初始值。

let source = Rx.Observable.of(1, 2, 3, 4, 5);
let target = source.scan((x, y) => x + y);
// 1-2-3-4-5-|
// scan
// 1-3-6-10-15-|

mergeMap/concatMap

该类映射方法包括两个步骤,首先将数据流中的每个元素再次映射为新的数据流,然后将多条新的数据流中的元素融合为一条数据流。参数为一个映射函数,返回值是一条数据流。

concatMap的区别在于融合不是按照时间,而是按照发射的先后顺序。即如果前者数据流还未complete后者数据流就开始流出数据的话,会延迟后者数据流的订阅时间直至前者complete,所以concatMap流出的数据严格按照输入顺序。而mergeMap则会出现混合顺序。

let source = Rx.Observable.of(10, 20, 30);
let target = source.flatMap(item => Rx.Observable.range(item, 3));
// 10-20-30-|
// mergeMap
// 10-11-12-20-21-22-30-31-32-|

-- EOF --

添加在分类「 前端开发 」下,并被添加 「RxJS」 标签。