2.1 创建操作符

153 阅读2分钟

创建操作符

interval

看名字不难理解,当被订阅时,会按照参数设定的时间(毫秒)间隔持续推送数据流,数据流从0开始。


import { interval } from 'rxjs';

interval(1000).subscribe(data => console.log(`interval 示例: ${data}`));

在取消订阅前,事件都会持续发生,弹珠图:


----0----1----2----3----.......

timer

timerinterval很类似,interval内部就是调用timer实现的。interval有个小缺点,就是一开始会先等待一个指定时间,才会发生第一个事件,有时候我们可能会希望一开始就发生事件,这个问题可通过timer解决。只要等待时间高为0即可


import { timer } from 'rxjs';

timer(0, 10000).subscribe(data => console.log(`timer 示例: ${data}`));

弹珠图:


0----1----2----3----......

还有一个重点,timer如果没有设定第二个参数,代表在指定的时间发生一次事后,就会结束


timer(3000).subscribe(data => {

console.log(`timer 一个参数示例: ${data}`);

});

弹珠图:


--------------------0|

PS: interval是直接调用timer实现的,我们看一下源码片段


// interval code fragment

export function interval(period = 0, scheduler: SchedulerLike = asyncScheduler): Observable<number> {

if (period < 0) {

// We cannot schedule an interval in the past.

period = 0;

}

// interval 调用timer时,第二个参数有个默认值

return timer(period, period, scheduler);

}

// timer code fragment

export function timer(

dueTime: number | Date = 0,

intervalOrScheduler?: number | SchedulerLike,

scheduler: SchedulerLike = asyncScheduler

): Observable<number> {

// Since negative intervalDuration is treated as though no

// interval was specified at all, we start with a negative number.

let intervalDuration = -1;

if (intervalOrScheduler != null) {

// If we have a second argument, and it's a scheduler,

// override the scheduler we had defaulted. Otherwise,

// it must be an interval.

if (isScheduler(intervalOrScheduler)) {

scheduler = intervalOrScheduler;

} else {

// Note that this *could* be negative, in which case

// it's like not passing an intervalDuration at all.

intervalDuration = intervalOrScheduler;

}

}

return new Observable((subscriber) => {

// If a valid date is passed, calculate how long to wait before

// executing the first value... otherwise, if it's a number just schedule

// that many milliseconds (or scheduler-specified unit size) in the future.

let due = isValidDate(dueTime) ? +dueTime - scheduler!.now() : dueTime;

if (due < 0) {

// Ensure we don't schedule in the future.

due = 0;

}

// The incrementing value we emit.

let n = 0;

// Start the timer.

return scheduler.schedule(function () {

if (!subscriber.closed) {

// Emit the next value and increment.

subscriber.next(n++);

// intervalDuration 是第二个参数,没有传入的话 直接next()以后执行complete()

if (0 <= intervalDuration) {

// If we have a interval after the initial timer,

// reschedule with the period.

this.schedule(undefined, intervalDuration);

} else {

// We didn't have an interval. So just complete.

subscriber.complete();

}

}

}, due);

});

}

of

将传进去的数据当作一条Observable,当数据都推送完后结束。


import { of } from 'rxjs';

of(1).subsctibe(data => console.log(`of 示例: ${data}`));

生成的弹珠图:


(1|)

也就是说立刻推送1的数据,然后立即结束complete()

of 也可以传入多个值,当订阅发生是这些值会各自触发next(),然后结束


of(1, 2, 3, 4).subscribe(data => console.log(`of 示例: ${data}`));

弹珠图:


(1234|)

EMPTY

顾名思义,就是一个空的Observable,没有任何事件就直接结束了


mport { EMPTY } from 'rxjs';

EMPTY.subscribe(data => data => console.log(`empty 示例: ${data}`));

// 不会有任何打印

range

顾名思义就是按照一个范围内的数列数据创建Observable,有两个参数

  • start: 从哪个数值开始

  • count: 建立多少个数值的数列


import { range } from 'rxjs';

range(3, 4).subscribe(data => console.log(`range 示例: ${data}`));

// 3 4 5 6

弹珠图:


(3456|)

iif

会通过条件来决定产生怎样的Observable,有三个参数

  • condition:传一个function,这个function会返回boolean

  • trueResult:当调用condition函数返回true时,使用trueResultObservable

  • falseResult:当调用condition函数返回false时,使用falseResultObservable


import { iif, of, EMPTY } from 'rxjs';

const iifHandle = (data) => {

return iif(() => data % 2 === 0, of("Hello"), EMPTY)

};

iifHandle(1).subscribe((data) =>

console.log(`iif 示例 (1): ${data}`)

);

// (不会打印任何东西)

iifHandle(2).subscribe((data) =>

console.log(`iif 示例 (2): ${data}`)

);

// iif 示例 (2): Hello

上边代码的关键在于:


iif(() => data % 2 === 0, of("Hello"), EMPTY)

当传入的data是偶数时,返回of('Hello')这个Observable,若不是则不做任何事情,也就是返回EMPTY这个Observable

throwError

看名字应该不难理解,用来让整个Observable发生错误用的。因此订阅时要记得使用error来处理,同时当有错误发生时,就不会有complete发生


const source = throwError("这是一个错误消息");

source.subscribe({

next: (data) => console.log(`throwError 示例 (next): ${data}`),

error: (error) => console.log(`throwError 示例 (error): ${error}`),

complete: () => console.log("throwError 示例 (complete)"),

});

// throwError 示例 (error): 这是一个错误消息

throwError一般不会单独使用,而是在使用pipe设计整个Observable时,用来处理错误的

from

可以接受的参数类型包含Array,Promise,Iterable,Observable对象,与上面的of非常的像

  • 传入Array

import { from } from 'rxjs';

from([1, 2, 3, 4]).subscribe(data => {

console.log(`from 示例 (1): ${data}`);

});

弹珠图:


(1234|)

  • 传入Iterable

// 使用generator

function* range(start, end) {

for (let i = start; i <= end; ++i) {

yield i;

}

}

from(range(1, 4)).subscribe((data) => {

console.log(`from 示例 (2): ${data}`);

});

  • 传入Promise

from(Promise.resolve(1)).subscribe(data => {

console.log(`from 示例 (3): ${data}`);

});

  • 传入 Observable

// 会重新组成一个新的Observable

from(of(1, 2, 3, 4)).subscribe(data => {

console.log(`from 示例 (4): ${data}`)

});

fromEvent

将浏览器事件包装成Observable,有两个参数

  • target 需要监听的DOM元素

  • eventName 事件名称

使用非常简单


import { fromEvent } from 'rxjs';

fromEvent(document, "click").subscribe((data) => {

console.log("fromEvent 示例: document事件触发了");

});

fromEventPattern

可以根据需要自定义逻辑决定事件发生,只要我们装将逻辑写好就好,有两个参数

  • addHandler:当我们订阅时subscribe时,调用此方法决定如何处理事件逻辑

  • removeHandler:当我们取消订阅时unsubscribe时,调用些方法将原来的事件逻辑取消


import { fromEventPattern } from 'rxjs';

const addClickHandler = (handler) => {

console.log("fromEventPattern 示例: 自定义注册click事件");

document.addEventListener("click", (event) => handler(event));

};

const removeClickHandler = (handler) => {

console.log("fromEventPattern 示例: 取消自定义注册click事件");

document.removeEventListener("click", handler);

};

const source = fromEventPattern(addClickHandler, removeClickHandler);

const subscription = source.subscribe((event) =>

console.log("fromEventPattern 示例: 触发document click 事件", event)

);

setTimeout(() => {

subscription.unsubscribe();

}, 3000);

上面代码中, 我们声明了两个function,并接收hanler参数,这两个function可以通过这个handler(callback)来决定事件的发生或取消。之后使用fromEventPattern传入这两个function, 来完成一个Observable,当订阅Subscribe产生时,会产生handler并调用addClickHandler,并在事件发生时执行handlercallback function。然后三秒后调用unsubscribe来取消订阅,

看到这里,fromEventfromEventPattern有什么区别?

defer

defer会将建立的Observable的逻辑包装起来,提供更一致的使用感觉,使用defer时需要传一个factory function当作参数,这个function里面需要返回一个Observable或Promise,当defer创建的Observable被订阅时,会调用这个factroy function,并以返回的Observable当作数据流。

源码片断


export function defer<R extends ObservableInput<any>>(observableFactory: () => R): Observable<ObservedValueOf<R>> {

return new Observable<ObservedValueOf<R>>((subscriber) => {

// 被订阅才会执行observableFactory

innerFrom(observableFactory()).subscribe(subscriber);

});

示例


import { defer } from 'rxjs';

const factory = () => of(1, 2, 3);

const source = defer(factory);

source.subscribe((data) => console.log(`defer 示例: ${data}`));

上面这段代码不用defer也没问题


const factory = () => of(1, 2, 3);

factory().subscribe(data => console.log(`defer 示例: ${data}`));

那么问题来了,为什么还要用defer呢?我们先看一段代码


const p = new Promise((resolve) => {

console.log('Promise 执行了');

setTimeout(() => {

resolve(100);

}, 1000);

});

p.then(result => {

console.log(`Promise 处理结果: ${result}`);

});

// Promise 执行了

// Promise 处理结果: 100

可以看到Promise excute立即执行了如果此时我们不想让Promise excuete立即执行,做一个延迟lazy 执行那么就该defer上场了


const promiseFactory = () =>

new Promise((resolve) => {

console.log("Promise 执行了");

setTimeout(() => {

resolve(100);

}, 1000);

});

const deferSource = defer(promiseFactory);

// 此時 Promise 内 excute 不会立马执行

console.log("示范用 defer 解決 Promise 的问题:");

// 直到被订阅了才会执行 promise excute

deferSource.subscribe((result) => {

console.log(`Promise 结果: ${result}`);

});