) => {
75 | return attempts.pipe(
76 | mergeMap((error, i) => {
77 | const retryAttempt = i + 1;
78 | // 如果达到最大重试次数或响应的状态码
79 | // 不是我们想重试的,就抛出错误
80 | if (
81 | retryAttempt > maxRetryAttempts ||
82 | excludedStatusCodes.find(e => e === error.status)
83 | ) {
84 | return _throw(error);
85 | }
86 | console.log(
87 | `Attempt ${retryAttempt}: retrying in ${retryAttempt *
88 | scalingDuration}ms`
89 | );
90 | // 重试的时间间隔不断增长: 1秒、2秒,以此类推
91 | return timer(retryAttempt * scalingDuration);
92 | }),
93 | finalize(() => console.log('We are done!'))
94 | );
95 | };
96 | ```
97 |
98 | ```js
99 | import { Component, OnInit } from '@angular/core';
100 | import { catchError, retryWhen } from 'rxjs/operators';
101 | import { of } from 'rxjs';
102 | import { genericRetryStrategy } from './rxjs-utils';
103 | import { AppService } from './app.service';
104 |
105 | @Component({
106 | selector: 'my-app',
107 | templateUrl: './app.component.html',
108 | styleUrls: [ './app.component.css' ]
109 | })
110 | export class AppComponent implements OnInit {
111 | constructor(private _appService: AppService) {}
112 |
113 | ngOnInit() {
114 | this._appService
115 | .getData(500)
116 | .pipe(
117 | retryWhen(genericRetryStrategy()),
118 | catchError(error => of(error))
119 | )
120 | .subscribe(console.log);
121 |
122 | // 排除状态码,增加延迟以保持日志清晰
123 | setTimeout(() => {
124 | this._appService
125 | .getData(500)
126 | .pipe(
127 | retryWhen(genericRetryStrategy({
128 | scalingDuration: 2000,
129 | excludedStatusCodes: [500]
130 | })),
131 | catchError(error => of(error))
132 | )
133 | .subscribe(e => console.log('Exluded code:', e.status));
134 |
135 | }, 8000);
136 | }
137 | }
138 | ```
139 |
140 | ### 其他资源
141 |
142 | - [retryWhen](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-retryWhen) :newspaper: - 官方文档
143 | - [错误处理操作符: retry 和 retryWhen](https://egghead.io/lessons/rxjs-error-handling-operator-retry-and-retrywhen?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
144 |
145 | ---
146 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/retryWhen.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/retryWhen.ts)
147 |
--------------------------------------------------------------------------------
/operators/filtering/README.md:
--------------------------------------------------------------------------------
1 | # 过滤操作符
2 |
3 | 在[基于推送的方式](http://reactivex.io/rxjs/manual/overview.html#pull-versus-push)下,选择接受项的方式和时间很重要。这些操作符提供了从 observable 源中接受值和处理[backpressure (背压)](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/backpressure.md)的技术。
4 |
5 | ## 内容
6 |
7 | * [audit](audit.md)
8 | * [auditTime](audittime.md)
9 | * [debounce](debounce.md)
10 | * [debounceTime](debouncetime.md) :star:
11 | * [distinctUntilChanged](distinctuntilchanged.md) :star:
12 | * [filter](filter.md) :star:
13 | * [first](first.md)
14 | * [ignoreElements](ignoreelements.md)
15 | * [last](last.md)
16 | * [sample](sample.md)
17 | * [single](single.md)
18 | * [skip](skip.md)
19 | * [skipUntil](skipuntil.md)
20 | * [skipWhile](skipwhile.md)
21 | * [take](take.md) :star:
22 | * [takeUntil](takeuntil.md) :star:
23 | * [takeWhile](takewhile.md)
24 | * [throttle](throttle.md)
25 | * [throttleTime](throttletime.md)
26 |
27 | :star: - *常用*
28 |
29 |
--------------------------------------------------------------------------------
/operators/filtering/audit.md:
--------------------------------------------------------------------------------
1 | # audit
2 |
3 | #### 函数签名: `audit(durationSelector: (value) => Observable | Promise): Observable`
4 |
5 | ## 根据提供的 Observable 来忽略某段时间内发出的数据,时间到后发出最新值
6 |
7 | ### [ 示例尽请期待! ]
8 |
9 | ### 其他资源
10 |
11 | * [audit](http://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-audit)
12 | :newspaper: - 官方文档
13 |
14 | ---
15 |
16 | > :file_folder: 源码:
17 | > [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/audit.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/audit.ts)
18 |
--------------------------------------------------------------------------------
/operators/filtering/audittime.md:
--------------------------------------------------------------------------------
1 | # auditTime
2 |
3 | #### 函数签名: `audit(duration: number, scheduler?: Scheduler): Observable`
4 |
5 | ## 在给定的时间内忽略发出的数据,时间到后发出最新值
6 |
7 | ### [ 示例尽请期待! ]
8 |
9 | ### 其他资源
10 |
11 | - [auditTime](http://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-auditTime)
12 | :newspaper: - 官方文档
13 |
14 | ---
15 |
16 | > :file_folder: 源码:
17 | > [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/auditTime.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/auditTime.ts)
18 |
--------------------------------------------------------------------------------
/operators/filtering/debounce.md:
--------------------------------------------------------------------------------
1 | # debounce
2 |
3 | #### 函数签名: `debounce(durationSelector: function): Observable`
4 |
5 | ## 根据一个选择器函数,舍弃掉在两次输出之间小于指定时间的发出值。
6 |
7 | ---
8 |
9 | :bulb: 尽管没有 [debounceTime](debouncetime.md) 使用广泛,但当 debounce 的频率是变量时, **debounce** 是很重要的!
10 |
11 | ---
12 |
13 | ### 示例
14 |
15 | ##### 示例 1: 基于 timer 的 debounce
16 |
17 | ( [StackBlitz](https://stackblitz.com/edit/typescript-dzjbra?file=index.ts&devtoolsheight=100) | [jsBin](http://jsbin.com/sorimeyoro/1/edit?js,console) |
18 | [jsFiddle](https://jsfiddle.net/btroncone/e5698yow/) )
19 |
20 | ```js
21 | // RxJS v6+
22 | import { of, timer } from 'rxjs';
23 | import { debounce } from 'rxjs/operators';
24 |
25 | // 发出四个字符串
26 | const example = of('WAIT', 'ONE', 'SECOND', 'Last will display');
27 | /*
28 | 只有在最后一次发送后再经过一秒钟,才会发出值,并抛弃在此之前的所有其他值
29 | */
30 | const debouncedExample = example.pipe(debounce(() => timer(1000)));
31 | /*
32 | 在这个示例中,所有的值都将被忽略,除了最后一个
33 | 输出: 'Last will display'
34 | */
35 | const subscribe = debouncedExample.subscribe(val => console.log(val));
36 | ```
37 |
38 | ##### 示例 2: 基于 interval 的 dobounce
39 |
40 | ( [StackBlitz](https://stackblitz.com/edit/typescript-qnfidr?file=index.ts&devtoolsheight=100) | [jsBin](http://jsbin.com/sotaretese/1/edit?js,console) |
41 | [jsFiddle](https://jsfiddle.net/btroncone/6ab34nq6/) )
42 |
43 | ```js
44 | // RxJS v6+
45 | import { interval, timer } from 'rxjs';
46 | import { debounce } from 'rxjs/operators';
47 |
48 | // 每1秒发出值, 示例: 0...1...2
49 | const interval$ = interval(1000);
50 | // 每1秒都将 debounce 的时间增加200毫秒
51 | const debouncedInterval = interval$.pipe(debounce(val => timer(val * 200)));
52 | /*
53 | 5秒后,debounce 的时间会大于 interval 的时间,之后所有的值都将被丢弃
54 | 输出: 0...1...2...3...4......(debounce 的时间大于1秒后则不再发出值)
55 | */
56 | const subscribe = debouncedInterval.subscribe(val =>
57 | console.log(`Example Two: ${val}`)
58 | );
59 | ```
60 |
61 | ### 其他资源
62 |
63 | - [debounce](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-debounce) :newspaper: - 官方文档
64 | - [转换操作符: debounce 和 debounceTime](https://egghead.io/lessons/rxjs-transformation-operators-debounce-and-debouncetime?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
65 |
66 | ---
67 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/debounce.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/debounce.ts)
68 |
--------------------------------------------------------------------------------
/operators/filtering/debouncetime.md:
--------------------------------------------------------------------------------
1 | # debounceTime
2 |
3 | #### 函数签名: `debounceTime(dueTime: number, scheduler: Scheduler): Observable`
4 |
5 | ## 舍弃掉在两次输出之间小于指定时间的发出值
6 |
7 | ---
8 |
9 | :bulb: 此操作符在诸如预先知道用户的输入频率的场景下很受欢迎!
10 |
11 | ---
12 |
13 | ### 示例
14 |
15 | ##### 示例 1: 基于输入间隔时间的 debounce
16 |
17 | ( [StackBlitz](https://stackblitz.com/edit/typescript-adheqt?file=index.ts&devtoolsheight=50) | [jsBin](http://jsbin.com/kacijarogi/1/edit?js,console,output) |
18 | [jsFiddle](https://jsfiddle.net/btroncone/7kbg4q2e/) )
19 |
20 | ```js
21 | // RxJS v6+
22 | import { fromEvent, timer } from 'rxjs';
23 | import { debounceTime, map } from 'rxjs/operators';
24 |
25 | const input = document.getElementById('example');
26 |
27 | // 对于每次键盘敲击,都将映射成当前输入值
28 | const example = fromEvent(input, 'keyup').pipe(map(i => i.currentTarget.value));
29 |
30 | // 在两次键盘敲击之间等待0.5秒方才发出当前值,
31 | // 并丢弃这0.5秒内的所有其他值
32 | const debouncedInput = example.pipe(debounceTime(500));
33 |
34 | // 输出值
35 | const subscribe = debouncedInput.subscribe(val => {
36 | console.log(`Debounced Input: ${val}`);
37 | });
38 | ```
39 |
40 | ### 其他资源
41 |
42 | * [debounceTime](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-debounceTime) :newspaper: - 官方文档
43 | * [转换操作符: debounce 和 debounceTime](https://egghead.io/lessons/rxjs-transformation-operators-debounce-and-debouncetime?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
44 |
45 | ---
46 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/debounceTime.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/debounceTime.ts)
47 |
--------------------------------------------------------------------------------
/operators/filtering/distinctuntilchanged.md:
--------------------------------------------------------------------------------
1 | # distinctUntilChanged
2 |
3 | #### 函数签名: ` distinctUntilChanged(compare: function): Observable`
4 |
5 | ## 只有当当前值与之前最后一个值不同时才将其发出。
6 |
7 | ---
8 |
9 | :bulb: distinctUntilChanged 默认使用 `===` 进行比较, 对象引用必须匹配!
10 |
11 | ---
12 |
13 | ### 示例
14 |
15 | ##### 示例 1: 使用基础值进行 distinctUntilChanged
16 |
17 | ( [StackBlitz](https://stackblitz.com/edit/typescript-bsb8mw?file=index.ts&devtoolsheight=100) | [jsBin](http://jsbin.com/qoyoxeheva/1/edit?js,console) |
18 | [jsFiddle](https://jsfiddle.net/btroncone/xc2vzct7/) )
19 |
20 | ```js
21 | // RxJS v6+
22 | import { from } from 'rxjs';
23 | import { distinctUntilChanged } from 'rxjs/operators';
24 |
25 | // 基于最新发出的值进行比较,只输出不同的值
26 | const myArrayWithDuplicatesInARow = from([1, 1, 2, 2, 3, 1, 2, 3]);
27 |
28 | const distinctSub = myArrayWithDuplicatesInARow
29 | .pipe(distinctUntilChanged())
30 | // 输出: 1,2,3,1,2,3
31 | .subscribe(val => console.log('DISTINCT SUB:', val));
32 |
33 | const nonDistinctSub = myArrayWithDuplicatesInARow
34 | // 输出 : 1,1,2,2,3,1,2,3
35 | .subscribe(val => console.log('NON DISTINCT SUB:', val));
36 | ```
37 |
38 | ##### 示例 2: 使用对象进行 distinctUntilChanged
39 |
40 | ( [StackBlitz](https://stackblitz.com/edit/typescript-moe7mh?file=index.ts&devtoolsheight=100) | [jsBin](http://jsbin.com/mexocipave/1/edit?js,console) |
41 | [jsFiddle](https://jsfiddle.net/btroncone/t4ava5b4/) )
42 |
43 | ```js
44 | // RxJS v6+
45 | import { from } from 'rxjs';
46 | import { distinctUntilChanged } from 'rxjs/operators';
47 |
48 | const sampleObject = { name: 'Test' };
49 | // 对象必须有相同的引用
50 | const myArrayWithDuplicateObjects = from([
51 | sampleObject,
52 | sampleObject,
53 | sampleObject
54 | ]);
55 | // 基于最新发出的值进行比较,只输出不同的对象
56 | const nonDistinctObjects = myArrayWithDuplicateObjects
57 | .pipe(distinctUntilChanged())
58 | // 输出: 'DISTINCT OBJECTS: {name: 'Test'}
59 | .subscribe(val => console.log('DISTINCT OBJECTS:', val));
60 | ```
61 |
62 | ### 其他资源
63 |
64 | * [distinctUntilChanged](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-distinctUntilChanged) :newspaper: - 官方文档
65 | * [过滤操作符: distinct 和 distinctUntilChanged](https://egghead.io/lessons/rxjs-filtering-operators-distinct-and-distinctuntilchanged?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
66 |
67 | ---
68 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/distinctUntilChanged.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/distinctUntilChanged.ts)
69 |
--------------------------------------------------------------------------------
/operators/filtering/filter.md:
--------------------------------------------------------------------------------
1 | # filter
2 |
3 | #### 函数签名: `filter(select: Function, thisArg: any): Observable`
4 |
5 | ## 发出符合给定条件的值。
6 |
7 | ---
8 |
9 | :bulb: 如果你希望当不满足条件时完成 observable 的话,请查阅 [takeWhile](takewhile.md)!
10 |
11 | ---
12 |
13 | ### 示例
14 |
15 | ##### 示例 1: 过滤出偶数
16 |
17 | ( [StackBlitz](https://stackblitz.com/edit/typescript-4g4cys?file=index.ts&devtoolsheight=100) | [jsBin](http://jsbin.com/vafogoluye/1/edit?js,console) |
18 | [jsFiddle](https://jsfiddle.net/btroncone/tkz0fuy2/) )
19 |
20 | ```js
21 | // RxJS v6+
22 | import { from } from 'rxjs';
23 | import { filter } from 'rxjs/operators';
24 |
25 | // 发出 (1,2,3,4,5)
26 | const source = from([1, 2, 3, 4, 5]);
27 | // 过滤掉奇数
28 | const example = source.pipe(filter(num => num % 2 === 0));
29 | // 输出: "Even number: 2", "Even number: 4"
30 | const subscribe = example.subscribe(val => console.log(`Even number: ${val}`));
31 | ```
32 |
33 | ##### 示例 2: 基于属性过滤对象
34 |
35 | ( [StackBlitz](https://stackblitz.com/edit/typescript-n73fsn?file=index.ts&devtoolsheight=100) | [jsBin](http://jsbin.com/qihagaxuso/1/edit?js,console) |
36 | [jsFiddle](https://jsfiddle.net/btroncone/yjdsoug1/) )
37 |
38 | ```js
39 | // RxJS v6+
40 | import { from } from 'rxjs';
41 | import { filter } from 'rxjs/operators';
42 |
43 | // 发出 ({name: 'Joe', age: 31}, {name: 'Bob', age:25})
44 | const source = from([{ name: 'Joe', age: 31 }, { name: 'Bob', age: 25 }]);
45 | // 过滤掉年龄小于30岁的人
46 | const example = source.pipe(filter(person => person.age >= 30));
47 | // 输出: "Over 30: Joe"
48 | const subscribe = example.subscribe(val => console.log(`Over 30: ${val.name}`));
49 | ```
50 |
51 | ##### 示例 3: 过滤出大于给定值的数字
52 |
53 | ( [StackBlitz](https://stackblitz.com/edit/typescript-eyvvfu?file=index.ts&devtoolsheight=100) | [jsBin](http://jsbin.com/rakabaheyu/1/edit?js,console) |
54 | [jsFiddle](https://jsfiddle.net/btroncone/g1tgreha/) )
55 |
56 | ```js
57 | // RxJS v6+
58 | import { interval } from 'rxjs';
59 | import { filter } from 'rxjs/operators';
60 |
61 | // 每1秒发出值
62 | const source = interval(1000);
63 | // 过滤掉所有值知道 interval 发出的值大于5
64 | const example = source.pipe(filter(num => num > 5));
65 | /*
66 | "Number greater than 5: 6"
67 | "Number greater than 5: 7"
68 | "Number greater than 5: 8"
69 | "Number greater than 5: 9"
70 | */
71 | const subscribe = example.subscribe(val =>
72 | console.log(`Number greater than 5: ${val}`)
73 | );
74 | ```
75 |
76 | ### 相关食谱
77 |
78 | * [HTTP 轮询](../../recipes/http-polling.md)
79 | * [游戏循环](../../recipes/gameloop.md)
80 |
81 | ### 其他资源
82 |
83 | * [filter](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-filter) :newspaper: - 官方文档
84 | * [使用 filter 添加条件逻辑](https://egghead.io/lessons/rxjs-adding-conditional-logic-with-filter?course=step-by-step-async-javascript-with-rxjs) :video_camera: :dollar: - John Linquist
85 | * [过滤操作符: filter](https://egghead.io/lessons/rxjs-filtering-operator-filter?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
86 |
87 | ---
88 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/filter.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/filter.ts)
89 |
--------------------------------------------------------------------------------
/operators/filtering/first.md:
--------------------------------------------------------------------------------
1 | # first
2 |
3 | #### 函数签名: `first(predicate: function, select: function)`
4 |
5 | ## 发出第一个值或第一个通过给定表达式的值。
6 |
7 | ---
8 |
9 | :bulb: 与 first 对应的操作符是 [**last**](last.md)!
10 |
11 | ---
12 |
13 |
14 |
15 | ### 示例
16 |
17 | (
18 | [example tests](https://github.com/btroncone/learn-rxjs/blob/master/operators/specs/filtering/first-spec.ts)
19 | )
20 |
21 | ##### 示例 1: 序列中的第一个值
22 |
23 | (
24 | [StackBlitz](https://stackblitz.com/edit/typescript-t8hseq?file=index.ts&devtoolsheight=100)
25 | | [jsBin](http://jsbin.com/kayenuxoma/1/edit?js,console) |
26 | [jsFiddle](https://jsfiddle.net/btroncone/uncey4v9/) )
27 |
28 | ```js
29 | // RxJS v6+
30 | import { from } from 'rxjs';
31 | import { first } from 'rxjs/operators';
32 |
33 | const source = from([1, 2, 3, 4, 5]);
34 | // 没有参数则发出第一个值
35 | const example = source.pipe(first());
36 | // 输出: "First value: 1"
37 | const subscribe = example.subscribe(val => console.log(`First value: ${val}`));
38 | ```
39 |
40 | ##### 示例 2: 第一个通过 predicate 函数的值
41 |
42 | (
43 | [StackBlitz](https://stackblitz.com/edit/typescript-bw5byu?file=index.ts&devtoolsheight=100)
44 | | [jsBin](http://jsbin.com/pujowawovu/1/edit?js,console) |
45 | [jsFiddle](https://jsfiddle.net/btroncone/pt36r8cu/) )
46 |
47 | ```js
48 | // RxJS v6+
49 | import { from } from 'rxjs';
50 | import { first } from 'rxjs/operators';
51 |
52 | const source = from([1, 2, 3, 4, 5]);
53 | // 发出通过测试的第一项
54 | const example = source.pipe(first(num => num === 5));
55 | // 输出: "First to pass test: 5"
56 | const subscribe = example.subscribe(val =>
57 | console.log(`First to pass test: ${val}`)
58 | );
59 | ```
60 |
61 | ##### 示例 3: 使用可选的 projection 函数
62 |
63 | (
64 | [StackBlitz](https://stackblitz.com/edit/typescript-2pkzpv?file=index.ts&devtoolsheight=100)
65 | | [jsBin](http://jsbin.com/qoganeleqa/1/edit?js,console) |
66 | [jsFiddle](https://jsfiddle.net/btroncone/owx2jdg1/3/) )
67 |
68 | ```js
69 | // RxJS v6+
70 | import { from } from 'rxjs';
71 | import { first } from 'rxjs/operators';
72 |
73 | const source = from([1, 2, 3, 4, 5]);
74 | // 没有值通过的话则发出默认值
75 | const example = source.pipe(first(val => val > 5, 'Nothing'));
76 | // 输出: 'Nothing'
77 | const subscribe = example.subscribe(val => console.log(val));
78 | ```
79 |
80 | ### 其他资源
81 |
82 | - [first](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-first) :newspaper: - 官方文档
83 | - [过滤操作符: take, first, skip](https://egghead.io/lessons/rxjs-filtering-operators-take-first-skip?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
84 |
85 | ---
86 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/first.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/first.ts)
87 |
--------------------------------------------------------------------------------
/operators/filtering/ignoreelements.md:
--------------------------------------------------------------------------------
1 | # ignoreElements
2 |
3 | #### 函数签名: `ignoreElements(): Observable`
4 |
5 | ## 忽略所有通知,除了 complete 和 error 。
6 |
7 |
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 忽略源 observable 的所有数据项
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-jpjcpg?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/yiyefelubi/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/59scjqss/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { interval } from 'rxjs';
21 | import { take, ignoreElements } from 'rxjs/operators';
22 |
23 | // 每100毫秒发出值
24 | const source = interval(100);
25 | // 略所有值,只发出 complete
26 | const example = source.pipe(
27 | take(5),
28 | ignoreElements()
29 | );
30 | // 输出: "COMPLETE!"
31 | const subscribe = example.subscribe(
32 | val => console.log(`NEXT: ${val}`),
33 | val => console.log(`ERROR: ${val}`),
34 | () => console.log('COMPLETE!')
35 | );
36 | ```
37 |
38 | ##### 示例 2: 只显示错误
39 |
40 | (
41 | [StackBlitz](https://stackblitz.com/edit/typescript-3yxv9z?file=index.ts&devtoolsheight=100)
42 | | [jsBin](http://jsbin.com/gogonawuze/1/edit?js,console) |
43 | [jsFiddle](https://jsfiddle.net/btroncone/srcwdgw6/) )
44 |
45 | ```js
46 | // RxJS v6+
47 | import { interval, throwError, of } from 'rxjs';
48 | import { mergeMap, ignoreElements } from 'rxjs/operators';
49 |
50 | // 每100毫秒发出值
51 | const source = interval(100);
52 | // 忽略所有值,只发出 error
53 | const error = source.pipe(
54 | mergeMap(val => {
55 | if (val === 4) {
56 | return throwError(`ERROR AT ${val}`);
57 | }
58 | return of(val);
59 | }),
60 | ignoreElements()
61 | );
62 | // 输出: "ERROR: ERROR AT 4"
63 | const subscribe = error.subscribe(
64 | val => console.log(`NEXT: ${val}`),
65 | val => console.log(`ERROR: ${val}`),
66 | () => console.log('SECOND COMPLETE!')
67 | );
68 | ```
69 |
70 | ### 其他资源
71 |
72 | - [ignoreElements](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-ignoreElements) :newspaper: - 官方文档
73 |
74 | ---
75 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/ignoreElements.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/ignoreElements.ts)
76 |
--------------------------------------------------------------------------------
/operators/filtering/last.md:
--------------------------------------------------------------------------------
1 | # last
2 |
3 | #### 函数签名: `last(predicate: function): Observable`
4 |
5 | ## 根据提供的表达式,在源 observable 完成时发出它的最后一个值。
6 |
7 | ---
8 |
9 | :bulb: 与 last 对应的操作符是 [**first**](first.md)!
10 |
11 | ---
12 |
13 |
14 |
15 | ### 示例
16 |
17 | ##### 示例 1: 序列中的最后一个值
18 |
19 | (
20 | [StackBlitz](https://stackblitz.com/edit/typescript-ma7knv?file=index.ts&devtoolsheight=100)
21 | | [jsBin](http://jsbin.com/pevaqeloki/1/edit?js,console) |
22 | [jsFiddle](https://jsfiddle.net/btroncone/b05r434a/) )
23 |
24 | ```js
25 | // RxJS v6+
26 | import { from } from 'rxjs';
27 | import { last } 'rxjs/operators';
28 |
29 | const source = from([1, 2, 3, 4, 5]);
30 | // 没有参数则发出最后一个值
31 | const example = source.pipe(last());
32 | // 输出: "Last value: 5"
33 | const subscribe = example.subscribe(val => console.log(`Last value: ${val}`));
34 | ```
35 |
36 | ##### 示例 2: 最后一个通过 predicate 函数的值
37 |
38 | (
39 | [StackBlitz](https://stackblitz.com/edit/typescript-tk42hj?file=index.ts&devtoolsheight=100)
40 | | [jsBin](http://jsbin.com/yagexuwari/1/edit?js,console) |
41 | [jsFiddle](https://jsfiddle.net/btroncone/pkx2btsh/) )
42 |
43 | ```js
44 | // RxJS v6+
45 | import { from } from 'rxjs';
46 | import { last } 'rxjs/operators';
47 |
48 | const source = from([1, 2, 3, 4, 5]);
49 | // 发出最后一个偶数
50 | const exampleTwo = source.pipe(last(num => num % 2 === 0));
51 | // 输出: "Last to pass test: 4"
52 | const subscribeTwo = exampleTwo.subscribe(val =>
53 | console.log(`Last to pass test: ${val}`)
54 | );
55 | ```
56 |
57 | ##### 示例 3: 使用结果选择器的 last
58 |
59 | (
60 | [StackBlitz](https://stackblitz.com/edit/typescript-nrc1an?file=index.ts&devtoolsheight=100)
61 | | [jsBin](http://jsbin.com/fudubebabi/1/edit?js,console) |
62 | [jsFiddle](https://jsfiddle.net/btroncone/L7fbx3vp/) )
63 |
64 | ```js
65 | // RxJS v6+
66 | import { from } from 'rxjs';
67 | import { last } 'rxjs/operators';
68 |
69 | const source = from([1, 2, 3, 4, 5]);
70 | // 没有值通过的话则发出默认值
71 | const exampleTwo = source.pipe(last(v => v > 5, 'Nothing!'));
72 | // 输出: 'Nothing!'
73 | const subscribeTwo = exampleTwo.subscribe(val => console.log(val));
74 | ```
75 |
76 | ### 其他资源
77 |
78 | - [last](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-last) :newspaper: - 官方文档
79 | - [过滤操作符: takeLast, last](https://egghead.io/lessons/rxjs-filtering-operators-takelast-last?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
80 |
81 | ---
82 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/last.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/last.ts)
83 |
--------------------------------------------------------------------------------
/operators/filtering/sample.md:
--------------------------------------------------------------------------------
1 | # sample
2 |
3 | #### 函数签名: `sample(sampler: Observable): Observable`
4 |
5 | ## 当提供的 observable 发出时从源 observable 中取样。
6 |
7 |
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 每2秒对源 observable 取样
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-envpsp?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/gemebopifu/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/8wsbuvjb/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { interval } from 'rxjs';
21 | import { sample } 'rxjs/operators';
22 |
23 | // 每1秒发出值
24 | const source = interval(1000);
25 | // 每2秒对源 observable 最新发出的值进行取样
26 | const example = source.pipe(sample(interval(2000)));
27 | // 输出: 2..4..6..8..
28 | const subscribe = example.subscribe(val => console.log(val));
29 | ```
30 |
31 | ##### 示例 2: 当 interval 发出时对源 observable 取样
32 |
33 | (
34 | [StackBlitz](https://stackblitz.com/edit/typescript-sgat7t?file=index.ts&devtoolsheight=100)
35 | | [jsBin](http://jsbin.com/cunicepube/1/edit?js,console) |
36 | [jsFiddle](https://jsfiddle.net/btroncone/b33kg9dn/) )
37 |
38 | ```js
39 | // RxJS v6+
40 | import { interval, zip, from } from 'rxjs';
41 | import { sample } from 'rxjs/operators';
42 |
43 | const source = zip(
44 | // 发出 'Joe', 'Frank' and 'Bob' in sequence
45 | from(['Joe', 'Frank', 'Bob']),
46 | // 每2秒发出值
47 | interval(2000)
48 | );
49 | // 每2.5秒对源 observable 最新发出的值进行取样
50 | const example = source.pipe(sample(interval(2500)));
51 | // 输出: ["Joe", 0]...["Frank", 1]...........
52 | const subscribe = example.subscribe(val => console.log(val));
53 | ```
54 |
55 | ##### 示例 3: 区分拖拽和点击
56 |
57 | 来源: [Stack Overflow](https://stackoverflow.com/a/44865892/2774547)
58 | 作者: [Dorus](https://stackoverflow.com/users/402027/dorus)
59 |
60 | (
61 | [StackBlitz](https://stackblitz.com/edit/typescript-vk8p3e?file=index.ts&devtoolsheight=100)
62 | | [jsBin](http://jsbin.com/riwipicilo/1/edit?html,js,console,output) |
63 | [jsFiddle](https://jsfiddle.net/6yy6q0Lo/1/) )
64 |
65 | ```js
66 | // RxJS v6+
67 | import { fromEvent, merge } from 'rxjs';
68 | import { sample, mapTo } from 'rxjs/operators';
69 |
70 | const listener = merge(
71 | fromEvent(document, 'mousedown').pipe(mapTo(false)),
72 | fromEvent(document, 'mousemove').pipe(mapTo(true))
73 | )
74 | .pipe(sample(fromEvent(document, 'mouseup')))
75 | .subscribe(isDragging => {
76 | console.log('Were you dragging?', isDragging);
77 | });
78 | ```
79 |
80 | ### 其他资源
81 |
82 | - [sample](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-sample) :newspaper: - 官方文档
83 |
84 | ---
85 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/sample.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/sample.ts)
86 |
--------------------------------------------------------------------------------
/operators/filtering/single.md:
--------------------------------------------------------------------------------
1 | # single
2 |
3 | #### 函数签名: `single(a: Function): Observable`
4 |
5 | ## 发出通过表达式的单一项。
6 |
7 |
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 发出通过断言的第一个数字
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-qhynlr?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/solecibuza/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/26r5y90s/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { from } from 'rxjs';
21 | import { single } from 'rxjs/operators';
22 |
23 | // 发出 (1,2,3,4,5)
24 | const source = from([1, 2, 3, 4, 5]);
25 | // 发出匹配断言函数的一项
26 | const example = source.pipe(single(val => val === 4));
27 | // 输出: 4
28 | const subscribe = example.subscribe(val => console.log(val));
29 | ```
30 |
31 | ### 其他资源
32 |
33 | - [single](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-single) :newspaper: - 官方文档
34 |
35 | ---
36 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/single.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/single.ts)
37 |
--------------------------------------------------------------------------------
/operators/filtering/skip.md:
--------------------------------------------------------------------------------
1 | # skip
2 |
3 | #### 函数签名: `skip(the: Number): Observable`
4 |
5 | ## 跳过N个(由参数提供)发出值。
6 |
7 | ### 为什么使用 `skip`?
8 |
9 | `skip` 允许你忽略源 observable 开头的n个值。通常,当你总是想忽略 observable 的某些值时,应该使用 `skip` 。或许你不需要这些开头的值,或许你订阅了 `Replay` 或 `BehaviorSubject` 从而不需要初始值。如果你不关心开头的一组值,那就使用 `skip` 吧。
10 |
11 | 你可以使用 [`filter`](./filter.md) 加索引来模拟 `skip` 。例如 `.filter((val, index) => index > 1)` 。
12 |
13 |
14 |
15 | ### 示例
16 |
17 | ##### 示例 1: 在发送前跳过N个值
18 |
19 | (
20 | [StackBlitz](https://stackblitz.com/edit/typescript-o5ydjf?file=index.ts&devtoolsheight=100)
21 | | [jsBin](http://jsbin.com/hacepudabi/1/edit?js,console) |
22 | [jsFiddle](https://jsfiddle.net/btroncone/ar1eqbya/) )
23 |
24 | ```js
25 | // RxJS v6+
26 | import { interval } from 'rxjs';
27 | import { skip } from 'rxjs/operators';
28 |
29 | // 每1秒发出值
30 | const source = interval(1000);
31 | // 跳过前5个发出值
32 | const example = source.pipe(skip(5));
33 | // 输出: 5...6...7...8........
34 | const subscribe = example.subscribe(val => console.log(val));
35 | ```
36 |
37 | #### 示例 2: filter 特定用法的简写形式
38 |
39 | (
40 | [StackBlitz](https://stackblitz.com/edit/typescript-yl3ap1?file=index.ts&devtoolsheight=100)
41 | | [jsBin](http://jsbin.com/judamurego/edit?js,console) |
42 | [jsFiddle](https://jsfiddle.net/ElHuy/4jswLn3z/) )
43 |
44 | ```js
45 | // RxJS v6+
46 | import { from } from 'rxjs';
47 | import { skip, filter } from 'rxjs/operators';
48 |
49 | const numArrayObs = from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
50 |
51 | // 3,4,5...
52 | const skipObs = numArrayObs.pipe(skip(2)).subscribe(console.log);
53 |
54 | // 3,4,5...
55 | const filterObs = numArrayObs
56 | .pipe(filter((val, index) => index > 1))
57 | .subscribe(console.log);
58 |
59 | // 同样的输出!
60 | ```
61 |
62 | ### 其他资源
63 |
64 | - [skip](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-skip) :newspaper: - 官方文档
65 | - [过滤操作符: take, first, skip](https://egghead.io/lessons/rxjs-filtering-operators-take-first-skip?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
66 |
67 | ---
68 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/skip.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/skip.ts)
69 |
--------------------------------------------------------------------------------
/operators/filtering/skipuntil.md:
--------------------------------------------------------------------------------
1 | # skipUntil
2 |
3 | #### 函数签名: `skipUntil(the: Observable): Observable`
4 |
5 | ## 跳过源 observable 发出的值,直到提供的 observable 发出值。
6 |
7 | ### 示例
8 |
9 | ##### 示例 1: 跳过值直到另个 observable 发出值
10 |
11 | (
12 | [StackBlitz](https://stackblitz.com/edit/typescript-gs4mps?file=index.ts&devtoolsheight=100)
13 | | [jsBin](http://jsbin.com/tapizososu/1/edit?js,console) |
14 | [jsFiddle](https://jsfiddle.net/btroncone/xLu8nf77/) )
15 |
16 | ```js
17 | // RxJS v6+
18 | import { interval, timer } from 'rxjs';
19 | import { skipUntil } from 'rxjs/operators';
20 |
21 | // 每1秒发出值
22 | const source = interval(1000);
23 | // 跳过源 observable 发出的值,直到内部 observable 发出值 (6s后)
24 | const example = source.pipe(skipUntil(timer(6000)));
25 | // 输出: 5...6...7...8........
26 | const subscribe = example.subscribe(val => console.log(val));
27 | ```
28 |
29 | ### 其他资源
30 |
31 | - [skipUntil](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-skipUntil) :newspaper: - 官方文档
32 |
33 | ---
34 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/skipUntil.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/skipUntil.ts)
35 |
--------------------------------------------------------------------------------
/operators/filtering/skipwhile.md:
--------------------------------------------------------------------------------
1 | # skipWhile
2 |
3 | #### 函数签名: `skipWhile(predicate: Function): Observable`
4 |
5 | ## 跳过源 observable 发出的值,直到提供的表达式结果为 false 。
6 |
7 |
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 当值小于阈值的时候跳过
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-p5kapz?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/bemikuleya/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/3ymfxb09/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { interval } from 'rxjs';
21 | import { skipWhile } from 'rxjs/operators';
22 |
23 | // 每1秒发出值
24 | const source = interval(1000);
25 | // 当源 observable 发出的值小于5的时候,则跳过该值
26 | const example = source.pipe(skipWhile(val => val < 5));
27 | // 输出: 5...6...7...8........
28 | const subscribe = example.subscribe(val => console.log(val));
29 | ```
30 |
31 | ### 其他资源
32 |
33 | - [skipWhile](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-skipWhile) :newspaper: - 官方文档
34 |
35 | ---
36 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/skipWhile.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/skipWhile.ts)
37 |
--------------------------------------------------------------------------------
/operators/filtering/take.md:
--------------------------------------------------------------------------------
1 | # take
2 |
3 | #### 函数签名: ` take(count: number): Observable`
4 |
5 | ## 在完成前发出N个值(N由参数决定)。
6 |
7 | ### 为什么使用 `take`?
8 |
9 | 当只对开头的一组值感兴趣时,你想要的便是 `take` 操作符。也许你想看看当用户第一次进入页面时,用户首先点击的是什么,你想要订阅点击事件并只取首个值。举例来说,你想要观看赛跑,但其实你只对首先冲过终点的人感兴趣。此操作符很清晰明了,你想要取开头*n*个值。
10 |
11 | ---
12 |
13 | :bulb: 如果想基于某个逻辑或另一个 observable 来取任意数量的值,你可以 [takeUntil](takeuntil.md) 或 [takeWhile](takewhile.md)!
14 |
15 | :bulb: `take` 与 `skip` 是相反的,它接收起始的N个值,而 `skip` 会跳过起始的N个值。
16 |
17 | ---
18 |
19 |
20 |
21 | ### 示例
22 |
23 | ##### 示例 1: 从源 observable 中取第一个值
24 |
25 | (
26 | [StackBlitz](https://stackblitz.com/edit/typescript-uk92ax?file=index.ts&devtoolsheight=100)
27 | | [jsBin](http://jsbin.com/vaxitupiwi/1/edit?js,console) |
28 | [jsFiddle](https://jsfiddle.net/btroncone/f9bz0tr3/) )
29 |
30 | ```js
31 | // RxJS v6+
32 | import { of } from 'rxjs';
33 | import { take } from 'rxjs/operators';
34 |
35 | // 发出 1,2,3,4,5
36 | const source = of(1, 2, 3, 4, 5);
37 | // 取第一个发出的值然后完成
38 | const example = source.pipe(take(1));
39 | // 输出: 1
40 | const subscribe = example.subscribe(val => console.log(val));
41 | ```
42 |
43 | ##### 示例 2: 从源 observable 中取前5个值
44 |
45 | (
46 | [StackBlitz](https://stackblitz.com/edit/typescript-3ujuth?file=index.ts&devtoolsheight=100)
47 | | [jsBin](http://jsbin.com/kexenuzulu/edit?js,console) |
48 | [jsFiddle](https://jsfiddle.net/btroncone/g1fhxgua/) )
49 |
50 | ```js
51 | // RxJS v6+
52 | import { interval } from 'rxjs';
53 | import { take } from 'rxjs/operators';
54 |
55 | // 每1秒发出值
56 | const interval$ = interval(1000);
57 | // 取前5个发出的值
58 | const example = interval$.pipe(take(5));
59 | // 输出: 0,1,2,3,4
60 | const subscribe = example.subscribe(val => console.log(val));
61 | ```
62 |
63 | ##### 示例 3: 取得首次点击的坐标
64 |
65 | ([StackBlitz](https://stackblitz.com/edit/typescript-8g9xt5?file=index.ts&devtoolsheight=50)
66 | | [jsFiddle](https://jsfiddle.net/ElHuy/9c5j064x/))
67 |
68 | ```html
69 |
70 | Where would you click first?
71 |
72 | ```
73 |
74 | ```js
75 | // RxJS v6+
76 | import { fromEvent } from 'rxjs';
77 | import { take, tap } from 'rxjs/operators';
78 |
79 | const oneClickEvent = fromEvent(document, 'click').pipe(
80 | take(1),
81 | tap(v => {
82 | document.getElementById(
83 | 'locationDisplay'
84 | ).innerHTML = `Your first click was on location ${v.screenX}:${v.screenY}`;
85 | })
86 | );
87 |
88 | const subscribe = oneClickEvent.subscribe();
89 | ```
90 |
91 | ### 其他资源
92 |
93 | - [take](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-take) :newspaper: - 官方文档
94 | - [过滤操作符: take, first, skip](https://egghead.io/lessons/rxjs-filtering-operators-take-first-skip?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
95 |
96 | ---
97 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/take.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/take.ts)
98 |
--------------------------------------------------------------------------------
/operators/filtering/takeuntil.md:
--------------------------------------------------------------------------------
1 | # takeUntil
2 |
3 | #### 函数签名: ` takeUntil(notifier: Observable): Observable`
4 |
5 | ## 发出值,直到提供的 observable 发出值,它便完成。
6 |
7 | ---
8 |
9 | :bulb: 如果你只需要指定数量的值,试试 [take](take.md)!
10 |
11 | ---
12 |
13 | 
14 |
15 | ### 示例
16 |
17 | ##### 示例 1: 取值直到 timer 发出
18 |
19 | (
20 | [StackBlitz](https://stackblitz.com/edit/typescript-ujwjbg?file=index.ts&devtoolsheight=100)
21 | | [jsBin](http://jsbin.com/yevuhukeja/1/edit?js,console) |
22 | [jsFiddle](https://jsfiddle.net/btroncone/zbe9dzb9/) )
23 |
24 | ```js
25 | // RxJS v6+
26 | import { interval, timer } from 'rxjs';
27 | import { takeUntil } from 'rxjs/operators';
28 |
29 | // 每1秒发出值
30 | const source = interval(1000);
31 | // 5秒后发出值
32 | const timer$ = timer(5000);
33 | // 当5秒后 timer 发出值时, source 则完成
34 | const example = source.pipe(takeUntil(timer$));
35 | // 输出: 0,1,2,3
36 | const subscribe = example.subscribe(val => console.log(val));
37 | ```
38 |
39 | ##### 示例 2: 取前5个偶数
40 |
41 | (
42 | [StackBlitz](https://stackblitz.com/edit/typescript-djhv7s?file=index.ts&devtoolsheight=100)
43 | | [jsBin](http://jsbin.com/doquqecara/1/edit?js,console) |
44 | [jsFiddle](https://jsfiddle.net/btroncone/0dLeksLe/) )
45 |
46 | ```js
47 | // RxJS v6+
48 | import { interval } from 'rxjs/observable/interval';
49 | import { takeUntil, filter, scan, map, withLatestFrom } from 'rxjs/operators';
50 |
51 | // 每1秒发出值
52 | const source = interval(1000);
53 | // 是偶数吗?
54 | const isEven = val => val % 2 === 0;
55 | // 只允许是偶数的值
56 | const evenSource = source.pipe(filter(isEven));
57 | // 保存运行中的偶数数量
58 | const evenNumberCount = evenSource.pipe(scan((acc, _) => acc + 1, 0));
59 | // 不发出直到发出过5个偶数
60 | const fiveEvenNumbers = evenNumberCount.pipe(filter(val => val > 5));
61 |
62 | const example = evenSource.pipe(
63 | // 还给出当前偶数的数量以用于显示
64 | withLatestFrom(evenNumberCount),
65 | map(([val, count]) => `Even number (${count}) : ${val}`),
66 | // 当发出了5个偶数时,source 则完成
67 | takeUntil(fiveEvenNumbers)
68 | );
69 | /*
70 | Even number (1) : 0,
71 | Even number (2) : 2
72 | Even number (3) : 4
73 | Even number (4) : 6
74 | Even number (5) : 8
75 | */
76 | const subscribe = example.subscribe(val => console.log(val));
77 | ```
78 |
79 | ### 其他资源
80 |
81 | - [takeUntil](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-takeUntil) :newspaper: - 官方文档
82 | - [避免 takeUntil 泄露](https://blog.angularindepth.com/rxjs-avoiding-takeuntil-leaks-fb5182d047ef) -
83 | Angular in Depth
84 | - [使用 takeUntil 来停止流](https://egghead.io/lessons/rxjs-stopping-a-stream-with-takeuntil?course=step-by-step-async-javascript-with-rxjs) :video_camera: :dollar: - John Linquist
85 |
86 | ---
87 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/takeUntil.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/takeUntil.ts)
88 |
--------------------------------------------------------------------------------
/operators/filtering/takewhile.md:
--------------------------------------------------------------------------------
1 | # takeWhile
2 |
3 | #### 函数签名: `takeWhile(predicate: function(value, index): boolean): Observable`
4 |
5 | ## 发出值,直到提供的表达式结果为 false 。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 使用限定条件取值
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-af3hdf?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/zanefaqexu/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/yakd4jgc/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { of } from 'rxjs';
21 | import { takeWhile } from 'rxjs/operators';
22 |
23 | // 发出 1,2,3,4,5
24 | const source = of(1, 2, 3, 4, 5);
25 | // 允许值发出直到 source 中的值大于4,然后便完成
26 | const example = source.pipe(takeWhile(val => val <= 4));
27 | // 输出: 1,2,3,4
28 | const subscribe = example.subscribe(val => console.log(val));
29 | ```
30 |
31 | ##### 示例 2: takeWhile() 和 filter() 的区别
32 |
33 | (
34 | [StackBlitz](https://stackblitz.com/edit/typescript-roozza?file=index.ts&devtoolsheight=100)
35 | | [jsBin](http://jsbin.com/yatoqurewi/1/edit?js,console) |
36 | [jsFiddle](https://jsfiddle.net/r497jgw3/4/) )
37 |
38 | ```js
39 | // RxJS v6+
40 | import { of } from 'rxjs';
41 | import { takeWhile, filter } from 'rxjs/operators';
42 |
43 | // 发出 3, 3, 3, 9, 1, 4, 5, 8, 96, 3, 66, 3, 3, 3
44 | const source = of(3, 3, 3, 9, 1, 4, 5, 8, 96, 3, 66, 3, 3, 3);
45 |
46 | // 允许值通过直到源发出的值不等于3,然后完成
47 | // 输出: [3, 3, 3]
48 | source
49 | .pipe(takeWhile(it => it === 3))
50 | .subscribe(val => console.log('takeWhile', val));
51 |
52 | // 输出: [3, 3, 3, 3, 3, 3, 3]
53 | source
54 | .pipe(filter(it => it === 3))
55 | .subscribe(val => console.log('filter', val));
56 | ```
57 |
58 | ### 相关食谱
59 |
60 | - [智能计数器](../../recipes/smartcounter.md)
61 |
62 | ### 其他资源
63 |
64 | - [takeWhile](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-takeWhile) :newspaper: - 官方文档
65 | - [使用 takeWhile 完成流](https://egghead.io/lessons/rxjs-completing-a-stream-with-takewhile?course=step-by-step-async-javascript-with-rxjs) :video_camera: :dollar: - John Linquist
66 |
67 | ---
68 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/takeWhile.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/takeWhile.ts)
69 |
--------------------------------------------------------------------------------
/operators/filtering/throttle.md:
--------------------------------------------------------------------------------
1 | # throttle
2 |
3 | #### 函数签名: `throttle(durationSelector: function(value): Observable | Promise): Observable`
4 |
5 | ## 以某个时间间隔为阈值,在 `durationSelector` 完成前将抑制新值的发出\
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 节流2秒,时间由第2个 observable 决定
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-pfcmjw?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/wohefujipo/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/h8na4m0p/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { interval } from 'rxjs';
21 | import { throttle } from 'rxjs/operators';
22 |
23 | // 每1秒发出值
24 | const source = interval(1000);
25 | // 节流2秒后才发出最新值
26 | const example = source.pipe(throttle(val => interval(2000)));
27 | // 输出: 0...3...6...9
28 | const subscribe = example.subscribe(val => console.log(val));
29 | ```
30 |
31 | ##### 示例 2: 使用 promise 进行节流
32 |
33 | (
34 | [StackBlitz](https://stackblitz.com/edit/typescript-g74v3q?file=index.ts&devtoolsheight=100)
35 | | [jsBin](http://jsbin.com/seyaguwunu/1/edit?js,console) |
36 | [jsFiddle](https://jsfiddle.net/btroncone/w5Lbzz9f/) )
37 |
38 | ```js
39 | // RxJS v6+
40 | import { interval } from 'rxjs';
41 | import { throttle, map } from 'rxjs/operators';
42 |
43 | // 每1秒发出值
44 | const source = interval(1000);
45 | // 基于 source 自增地增加解析的时间
46 | const promise = val =>
47 | new Promise(resolve =>
48 | setTimeout(() => resolve(`Resolved: ${val}`), val * 100)
49 | );
50 | // 当 promise 解析时发出 source 的项
51 | const example = source.pipe(
52 | throttle(promise),
53 | map(val => `Throttled off Promise: ${val}`)
54 | );
55 |
56 | const subscribe = example.subscribe(val => console.log(val));
57 | ```
58 |
59 | ### 其他资源
60 |
61 | - [throttle](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-throttle) :newspaper: - 官方文档
62 | - [过滤操作符: throttle 和 throttleTime](https://egghead.io/lessons/rxjs-filtering-operators-throttle-and-throttletime?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
63 |
64 | ---
65 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/throttle.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/throttle.ts)
66 |
--------------------------------------------------------------------------------
/operators/filtering/throttletime.md:
--------------------------------------------------------------------------------
1 | # throttleTime
2 |
3 | #### 函数签名: `throttleTime(duration: number, scheduler: Scheduler): Observable`
4 |
5 | ## 当指定的持续时间经过后发出最新值。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 每5秒接收最新值
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-en2zqe?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/koqujayizo/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/4zysLc3y/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { interval } from 'rxjs';
21 | import { throttleTime } from 'rxjs/operators';
22 |
23 | // 每1秒发出值
24 | const source = interval(1000);
25 | /*
26 | 节流5秒
27 | 节流结束前发出的最后一个值将从源 observable 中发出
28 | */
29 | const example = source.pipe(throttleTime(5000));
30 | // 输出: 0...6...12
31 | const subscribe = example.subscribe(val => console.log(val));
32 | ```
33 |
34 | ##### 示例 2: 对合并的 observable 节流
35 |
36 | (
37 | [StackBlitz](https://stackblitz.com/edit/typescript-bkcjfj?file=index.ts&devtoolsheight=100)
38 | | [jsBin](http://jsbin.com/takipadaza/edit?js,console) |
39 | [jsFiddle](https://jsfiddle.net/btroncone/xhd1zy3m/) )
40 |
41 | ```js
42 | // RxJS v6+
43 | import { interval, merge } from 'rxjs';
44 | import { throttleTime, ignoreElements } from 'rxjs/operators';
45 |
46 | const source = merge(
47 | // 每0.75秒发出值
48 | interval(750),
49 | // 每1秒发出值
50 | interval(1000)
51 | );
52 | // 在发出值的中间进行节流
53 | const example = source.pipe(throttleTime(1200));
54 | // 输出: 0...1...4...4...8...7
55 | const subscribe = example.subscribe(val => console.log(val));
56 | ```
57 |
58 | ### 其他资源
59 |
60 | - [throttleTime](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-throttleTime) :newspaper: - 官方文档
61 | - [过滤操作符: throttle 和 throttleTime](https://egghead.io/lessons/rxjs-filtering-operators-throttle-and-throttletime?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
62 |
63 | ---
64 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/throttleTime.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/throttleTime.ts)
65 |
--------------------------------------------------------------------------------
/operators/multicasting/README.md:
--------------------------------------------------------------------------------
1 | # 多播操作符
2 |
3 | 在 RxJS 中,默认的 observables 是冷的或者单播的。这些操作符可以使 observable 变成热的或者多播的,以允许副作用可以在多个订阅者之间共享。
4 |
5 | ## 内容
6 |
7 | - [publish](publish.md)
8 | - [multicast](multicast.md)
9 | - [share](share.md) :star:
10 | - [shareReplay](sharereplay.md) :star:
11 |
12 | :star: - *常用*
13 |
14 | ### 其他资源
15 |
16 | - [热的 Observables vs 冷的 Observables](https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339#.8x9uam5rg) :newspaper: - Ben Lesh
17 | - [单播 v 多播](https://github.com/zenparsing/es-observable/issues/66) :newspaper: - GitHub 讨论
18 | - [热/冷的 Observables 揭秘](https://egghead.io/lessons/rxjs-demystifying-cold-and-hot-observables-in-rxjs) :video_camera: - André Staltz
19 |
--------------------------------------------------------------------------------
/operators/multicasting/multicast.md:
--------------------------------------------------------------------------------
1 | # multicast
2 |
3 | #### 函数签名: `multicast(selector: Function): Observable`
4 |
5 | ## 使用提供 的 Subject 来共享源 observable
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 使用标准的 Subject 进行 multicast
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-vge8sk?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/zexuyosuvi/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/x2z7p1gm/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { Subject, interval } from 'rxjs';
21 | import { take, tap, multicast, mapTo } from 'rxjs/operators';
22 |
23 | // 每2秒发出值并只取前5个
24 | const source = interval(2000).pipe(take(5));
25 |
26 | const example = source.pipe(
27 | // 因为我们在下面进行了多播,所以副作用只会调用一次
28 | tap(() => console.log('Side Effect #1')),
29 | mapTo('Result!')
30 | );
31 |
32 |
33 | // 使用 subject 订阅 source 需要调用 connect() 方法
34 | const multi = example.pipe(multicast(() => new Subject()));
35 | /*
36 | 多个订阅者会共享 source
37 | 输出:
38 | "Side Effect #1"
39 | "Result!"
40 | "Result!"
41 | ...
42 | */
43 | const subscriberOne = multi.subscribe(val => console.log(val));
44 | const subscriberTwo = multi.subscribe(val => console.log(val));
45 | // 使用 subject 订阅 source
46 | multi.connect();
47 | ```
48 |
49 | ##### 示例 2: 使用 ReplaySubject 进行 multicast
50 |
51 | (
52 | [StackBlitz](https://stackblitz.com/edit/typescript-n5ghjj?file=index.ts&devtoolsheight=100)
53 | | [jsBin](http://jsbin.com/ruhexuhike/1/edit?js,console) |
54 | [jsFiddle](https://jsfiddle.net/btroncone/oj68u58j/) )
55 |
56 | ```js
57 | // RxJS v6+
58 | import { interval, ReplaySubject } from 'rxjs';
59 | import { take, multicast, tap, mapTo } from 'rxjs/operators';
60 |
61 | // 每2秒发出值并只取前5个
62 | const source = interval(2000).pipe(take(5));
63 |
64 | // 使用 ReplaySubject 的示例
65 | const example = source.pipe(
66 | // 因为我们在下面进行了多播,所以副作用只会调用一次
67 | tap(_ => console.log('Side Effect #2')),
68 | mapTo('Result Two!')
69 | );
70 | // 可以使用任何类型的 subject
71 | const multi = example.pipe(multicast(() => new ReplaySubject(5)));
72 | // 使用 subject 订阅 source
73 | multi.connect();
74 |
75 | setTimeout(() => {
76 | /*
77 | 因为使用的是 ReplaySubject,订阅者会接收到 subscription 中的之前所有值。
78 | */
79 | const subscriber = multi.subscribe(val => console.group(val));
80 | }, 5000);
81 | ```
82 |
83 | ### 其他资源
84 |
85 | - [multicast](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-multicast) :newspaper: - 官方文档
86 |
87 | ---
88 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/multicast.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/multicast.ts)
89 |
--------------------------------------------------------------------------------
/operators/multicasting/publish.md:
--------------------------------------------------------------------------------
1 | # publish
2 |
3 | #### 函数签名: `publish() : ConnectableObservable`
4 |
5 | ## 共享源 observable 并通过调用 connect 方法使其变成热的。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 在订阅之后调用 observable 的 connect 方法
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-zje8ms?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/laguvecixi/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/fpe6csaz/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { interval } from 'rxjs';
21 | import { publish, tap } from 'rxjs/operators';
22 |
23 | // 每1秒发出值
24 | const source = interval(1000);
25 | const example = source.pipe(
26 | // 副作用只会执行1次
27 | tap(_ => console.log('Do Something!')),
28 | // 不会做任何事直到 connect() 被调用
29 | publish()
30 | );
31 |
32 | /*
33 | source 不会发出任何值直到 connect() 被调用
34 | 输出: (5秒后)
35 | "Do Something!"
36 | "Subscriber One: 0"
37 | "Subscriber Two: 0"
38 | "Do Something!"
39 | "Subscriber One: 1"
40 | "Subscriber Two: 1"
41 | */
42 | const subscribe = example.subscribe(val =>
43 | console.log(`Subscriber One: ${val}`)
44 | );
45 | const subscribeTwo = example.subscribe(val =>
46 | console.log(`Subscriber Two: ${val}`)
47 | );
48 |
49 | // 5秒后调用 connect,这会使得 source 开始发出值
50 | setTimeout(() => {
51 | example.connect();
52 | }, 5000);
53 | ```
54 |
55 |
56 | ### 其他资源
57 |
58 | - [publish](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-publish) :newspaper: - 官方文档
59 |
60 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/publish.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/publish.ts)
61 |
--------------------------------------------------------------------------------
/operators/multicasting/share.md:
--------------------------------------------------------------------------------
1 | # share
2 |
3 | #### 函数签名: `share(): Observable`
4 |
5 | ## 在多个订阅者间共享源 observable 。
6 |
7 | ---
8 |
9 | :bulb: share 就像是使用了 Subject 和 refCount 的 [multicast](multicast.md)!
10 |
11 | ---
12 |
13 | 
14 |
15 | ### 示例
16 |
17 | ##### 示例 1: 多个订阅者共享源 observable
18 |
19 | (
20 | [StackBlitz](https://stackblitz.com/edit/typescript-dlaa1p?file=index.ts&devtoolsheight=100)
21 | | [jsBin](http://jsbin.com/jobiyomari/1/edit?js,console) |
22 | [jsFiddle](https://jsfiddle.net/btroncone/Lmesxxaq/) )
23 |
24 | ```js
25 | // RxJS v6+
26 | import { timer } from 'rxjs';
27 | import { tap, mapTo, share } from 'rxjs/operators';
28 |
29 | // 1秒后发出值
30 | const source = timer(1000);
31 | // 输出副作用,然后发出结果
32 | const example = source.pipe(
33 | tap(() => console.log('***SIDE EFFECT***')),
34 | mapTo('***RESULT***')
35 | );
36 |
37 | /*
38 | ***不共享的话,副作用会执行两次***
39 | 输出:
40 | "***SIDE EFFECT***"
41 | "***RESULT***"
42 | "***SIDE EFFECT***"
43 | "***RESULT***"
44 | */
45 | const subscribe = example.subscribe(val => console.log(val));
46 | const subscribeTwo = example.subscribe(val => console.log(val));
47 |
48 | // 在多个订阅者间共享 observable
49 | const sharedExample = example.pipe(share());
50 | /*
51 | ***共享的话,副作用只执行一次***
52 | 输出:
53 | "***SIDE EFFECT***"
54 | "***RESULT***"
55 | "***RESULT***"
56 | */
57 | const subscribeThree = sharedExample.subscribe(val => console.log(val));
58 | const subscribeFour = sharedExample.subscribe(val => console.log(val));
59 | ```
60 |
61 | ### 相关食谱
62 |
63 | - [进度条](../../recipes/progressbar.md)
64 | - [游戏循环](../../recipes/gameloop.md)
65 |
66 | ### 其他资源
67 |
68 | ### 其他资源
69 |
70 | - [share](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-share) :newspaper: - 官方文档
71 | - [使用 share 共享流](https://egghead.io/lessons/rxjs-sharing-streams-with-share?course=step-by-step-async-javascript-with-rxjs) :video_camera: :dollar: - John Linquist
72 |
73 | ---
74 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/share.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/share.ts)
75 |
--------------------------------------------------------------------------------
/operators/specs/combination/combineall-spec.ts:
--------------------------------------------------------------------------------
1 | declare var describe, it, expect, hot, cold, expectObservable, expectSubscriptions, rxTestScheduler, beforeEach;
2 | import {Observable} from 'rxjs/Observable';
3 | import 'rxjs/add/observable/of';
4 | import 'rxjs/add/observable/interval';
5 | import 'rxjs/add/operator/map';
6 | import 'rxjs/add/operator/combineall';
7 | import 'rxjs/add/operator/take';
8 |
9 | describe('The combineAll operator examples', () => {
10 | describe('Example 1 - mapping to an inner observable', () => {
11 | it('should emit the result of both observables as an array', (done) => {
12 | let results = [];
13 | const source = Observable.interval(1000).take(2);
14 | const example = source.map(val => Observable.of(val));
15 | const combined = example.combineAll();
16 | const expected = [[0,1]];
17 | combined.subscribe({
18 | next: val => results.push(val),
19 | complete: () => {
20 | expect(results).toDeepEqual(expected);
21 | done();
22 | }
23 | });
24 | });
25 | });
26 | });
--------------------------------------------------------------------------------
/operators/specs/combination/combinelatest-spec.ts:
--------------------------------------------------------------------------------
1 | declare var describe, it, expect, hot, cold, expectObservable, expectSubscriptions, rxTestScheduler, beforeEach;
2 | import {Observable} from 'rxjs/Observable';
3 | import 'rxjs/add/observable/combinelatest';
4 |
5 | describe('The combineLatest operator examples', () => {
6 | describe('Example 1 - Combining observables emitting at 3 intervals', () => {
7 | it('should combine three observables emitting at seperate intervals', () => {
8 | const t1 = hot( 'a----b-----c---|');
9 | const t2 = hot( '-----e------f--------|');
10 | const t3 = hot( '-------------h-----i---------|')
11 | const expected = '-------------y-----z---------|';
12 | var combined = Observable.combineLatest(t1, t2, t3, (a,b,c) => a + b + c);
13 | expectObservable(combined).toBe(expected, { y: 'cfh', z: 'cfi'});
14 | });
15 | });
16 | });
--------------------------------------------------------------------------------
/operators/specs/combination/concat-spec.ts:
--------------------------------------------------------------------------------
1 | declare var describe, it, expect, hot, cold, expectObservable, expectSubscriptions, rxTestScheduler, beforeEach;
2 | import {Observable} from 'rxjs/Observable';
3 | import 'rxjs/add/observable/of';
4 | import 'rxjs/add/observable/concat';
5 | import 'rxjs/add/operator/concat';
6 | import 'rxjs/add/operator/delay';
7 |
8 | describe('The concat operator examples', () => {
9 | describe('Example 1 - concat 2 basic observables', () => {
10 | it('should emit values from sourceTwo after sourceOne', () => {
11 | const sourceOne = Observable.of(1,2,3);
12 | const sourceTwo = Observable.of(4,5,6);
13 | const example = sourceOne.concat(sourceTwo);
14 | const values = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6};
15 | const expected = '(abcdef|)';
16 |
17 | expectObservable(example).toBe(expected, values);
18 | });
19 | });
20 |
21 | describe('Example 2 - concat as static method', () => {
22 | it('should emit values from sourceTwo after sourceOne when used as static method', () => {
23 | const sourceOne = Observable.of(1,2,3);
24 | const sourceTwo = Observable.of(4,5,6);
25 | const example = Observable.concat(sourceOne,sourceTwo);
26 | const values = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6};
27 | const expected = '(abcdef|)';
28 |
29 | expectObservable(example).toBe(expected, values);
30 | });
31 | });
32 |
33 | describe('Example 3 - concat with a delayed source', () => {
34 | it('should emit values from sourceTwo after delayed sourceThree completes', () => {
35 | const sourceOne = Observable.of(1,2,3);
36 | const sourceTwo = Observable.of(4,5,6);
37 | const sourceThree = sourceOne.delay(20, rxTestScheduler);
38 | const example = sourceThree.concat(sourceTwo);
39 | const values = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6};
40 | const expected = '--(abcdef|)';
41 |
42 | expectObservable(example).toBe(expected, values);
43 | });
44 | });
45 | });
--------------------------------------------------------------------------------
/operators/specs/combination/concatall-spec.ts:
--------------------------------------------------------------------------------
1 | declare var describe, it, expect, hot, cold, expectObservable, expectSubscriptions, rxTestScheduler, beforeEach;
2 | import {Observable} from 'rxjs/Observable';
3 | import 'rxjs/add/observable/of';
4 | import 'rxjs/add/operator/concatAll';
5 | import 'rxjs/add/operator/map';
6 |
7 | describe('The concatAll operator examples', () => {
8 | describe('Example 1 - concatAll with observable', () => {
9 | it('should subscribe to inner observables, emitting the result', () => {
10 | const values = {
11 | a: 1,
12 | b: 2,
13 | c: 3,
14 | d: 1 + 10,
15 | e: 2 + 10,
16 | f: 3 + 10
17 | };
18 | const source = cold('---a---b---c|', values);
19 | const example = source
20 | .map(val => Observable.of(val + 10))
21 | .concatAll();
22 | const expected = '---d---e---f|';
23 |
24 | expectObservable(example).toBe(expected, values);
25 | });
26 | });
27 |
28 | describe('Example 2 - concatAll with promise', () => {
29 | it('should emit the result of promises', (done) => {
30 | let results = [];
31 | const samplePromise = val => new Promise(resolve => resolve(val + 10));
32 | const values = {
33 | a: 1,
34 | b: 2,
35 | c: 3,
36 | d: 1 + 10,
37 | e: 2 + 10,
38 | f: 3 + 10
39 | };
40 | const source = Observable.of(1,2,3);
41 | const example = source
42 | .map(val => samplePromise(val))
43 | .concatAll();
44 |
45 | example.subscribe({
46 | next: val => results.push(val),
47 | complete: () => {
48 | expect(results).toDeepEqual([11,12,13]);
49 | done();
50 | }
51 | });
52 | });
53 | });
54 | });
--------------------------------------------------------------------------------
/operators/specs/combination/mergeall-spec.ts:
--------------------------------------------------------------------------------
1 | declare var describe, it, expect, hot, cold, expectObservable, expectSubscriptions, rxTestScheduler, beforeEach;
2 | import {Observable} from 'rxjs/Observable';
3 | import 'rxjs/add/observable/of';
4 | import 'rxjs/add/operator/map';
5 | import 'rxjs/add/operator/mergeall';
6 |
7 | describe('The concat operator examples', () => {
8 | describe('Example 1 - mergeAll with promises', () => {
9 | it('should emit the result of promises', (done) => {
10 | const results = [];
11 | const myPromise = val => new Promise(resolve => resolve(`Result: ${val}`))
12 | const source = Observable.of(1,2,3);
13 | const example = source
14 | .map(val => myPromise(val))
15 | .mergeAll();
16 | const expected = ['Result: 1', 'Result: 2', 'Result: 3'];
17 | example.subscribe({
18 | next: val => results.push(val),
19 | complete: () => {
20 | expect(results).toDeepEqual(expected);
21 | done();
22 | }
23 | });
24 | });
25 | });
26 | });
--------------------------------------------------------------------------------
/operators/specs/combination/startwith-spec.ts:
--------------------------------------------------------------------------------
1 | declare var describe, it, expect, hot, cold, expectObservable, expectSubscriptions, rxTestScheduler, beforeEach;
2 | import {Observable} from 'rxjs/Observable';
3 | import 'rxjs/add/observable/interval';
4 | import 'rxjs/add/observable/of';
5 | import 'rxjs/add/observable/interval';
6 | import 'rxjs/add/operator/startWith';
7 | import 'rxjs/add/operator/scan';
8 | import 'rxjs/add/operator/take';
9 |
10 | describe('The startWith operator examples', () => {
11 | describe('Example 1 - startWith on number sequence', () => {
12 | it('should start the sequence with 0', () => {
13 | const source = Observable.of(1,2,3);
14 | const example = source.startWith(0);
15 | const values = {a: 0, b: 1, c: 2, d: 3};
16 | const expected = '(abcd|)';
17 |
18 | expectObservable(example).toBe(expected, values);
19 | });
20 | });
21 |
22 | describe('Example 2: startWith for initial scan value', () => {
23 | it('should start with initial scan value', () => {
24 | const source = Observable.of('World!', 'Goodbye', 'World!');
25 | const example = source
26 | .startWith('Hello')
27 | .scan((acc, curr) => `${acc} ${curr}`);
28 | const values = {a: 'Hello', b: 'Hello World!', c: 'Hello World! Goodbye', d: 'Hello World! Goodbye World!'};
29 | const expected = '(abcd|)';
30 |
31 | expectObservable(example).toBe(expected, values);
32 | });
33 | });
34 |
35 | describe('Example 3: startWith multiple values', () => {
36 | it('should start with -3, -2, -1', () => {
37 | //reducing interval for testing purposes
38 | const source = Observable.interval(-1, rxTestScheduler);
39 | const example = source.startWith(-3, -2, -1).take(4);
40 | const values = {a: -3, b: -2, c: -1, d: 0};
41 | const expected = '(abcd|))';
42 |
43 | expectObservable(example).toBe(expected, values);
44 | });
45 | });
46 | });
--------------------------------------------------------------------------------
/operators/specs/error_handling/catch-spec.ts:
--------------------------------------------------------------------------------
1 | declare var describe, it, expect, hot, cold, expectObservable, expectSubscriptions, rxTestScheduler, beforeEach;
2 | import {Observable} from 'rxjs/Observable';
3 | import 'rxjs/add/operator/catch';
4 | import 'rxjs/add/operator/mergeMap';
5 | import 'rxjs/add/observable/of';
6 |
7 | describe('The catch operator examples', () => {
8 |
9 | describe('Example 1 - Catching error from observable', () => {
10 | it('catch an error', () => {
11 | const source = cold('#')
12 | const example = source.catch(_ => Observable.of('Error Caught!'));
13 | const expected = '(a|)';
14 |
15 | expectObservable(example).toBe(expected, {a: 'Error Caught!'});
16 | });
17 | });
18 |
19 | describe('Example 2 - Catching rejected promise', () => {
20 | it('catch an error from a rejected promise', (done) => {
21 | let result;
22 | const badPromise = val => new Promise((resolve, reject) => reject('Rejected!'))
23 | const source = Observable.of(true);
24 | const example = source
25 | .mergeMap(val => badPromise(val))
26 | .catch(err => Observable.of(err));
27 |
28 | const expected = 'Rejected!';
29 |
30 | example.subscribe({
31 | next: val => result = val,
32 | complete: () => {
33 | expect(result).toBe('Rejected!');
34 | done();
35 | }
36 | });
37 | });
38 | });
39 | });
--------------------------------------------------------------------------------
/operators/specs/filtering/first-spec.ts:
--------------------------------------------------------------------------------
1 | declare var describe, it, expect, hot, cold, expectObservable, expectSubscriptions, rxTestScheduler, beforeEach;
2 | import {Observable} from 'rxjs/Observable';
3 | import 'rxjs/add/operator/first';
4 | import 'rxjs/add/operator/mergeMap';
5 | import 'rxjs/add/observable/of';
6 | import 'rxjs/add/observable/from';
7 |
8 | describe('The first operator examples', () => {
9 |
10 | describe('Example 1 - First value from sequence', () => {
11 | it('should emit the first value then complete', () => {
12 | const source = Observable.from([1,2,3,4,5]);
13 | const example = source.first();
14 | const expected = '(a|)';
15 |
16 | expectObservable(example).toBe(expected, {a: 1});
17 | });
18 | });
19 |
20 | describe('Example 2 - First value to pass predicate', () => {
21 | it('should emit the first value to pass predicate then complete', () => {
22 | const values = {a: 1, b: 2, c: 3, d: 4, e: 5};
23 | const source = cold('(abcde|)', values);
24 | const example = source.first(num => num === 5);
25 | const expected = '(e|)';
26 |
27 | expectObservable(example).toBe(expected, values)
28 | });
29 | });
30 |
31 | describe('Example 3 - Using optional projection function', () => {
32 | it('should emit the result of the projection function, given the first value', () => {
33 | const values = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 'First even: 2 is at index: 1'};
34 | const source = cold('(abcde|)', values);
35 | const example = source.first(num => num % 2 === 0,
36 | (result, index) => `First even: ${result} is at index: ${index}`);
37 | const expected = '(f|)';
38 |
39 | expectObservable(example).toBe(expected, values)
40 | });
41 | });
42 |
43 | describe('Example 4 - Utilizing default value', () => {
44 | it('should emit the default value when source completes and no values pass predicate', () => {
45 | const values = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 'Nothing'};
46 | const source = cold('(abcde|)', values);
47 | const example = source.first(val => val > 5, val => `Value: ${val}`, 'Nothing');
48 | const expected = '(f|)';
49 |
50 | expectObservable(example).toBe(expected, values)
51 | });
52 | });
53 | });
--------------------------------------------------------------------------------
/operators/specs/helpers/marble-testing.ts:
--------------------------------------------------------------------------------
1 | declare var global, expect;
2 |
3 | function hot() {
4 | if (!global.rxTestScheduler) {
5 | throw 'tried to use hot() in async test';
6 | }
7 | return global.rxTestScheduler.createHotObservable.apply(global.rxTestScheduler, arguments);
8 | }
9 |
10 | function cold() {
11 | if (!global.rxTestScheduler) {
12 | throw 'tried to use cold() in async test';
13 | }
14 | return global.rxTestScheduler.createColdObservable.apply(global.rxTestScheduler, arguments);
15 | }
16 |
17 | function expectObservable() {
18 | if (!global.rxTestScheduler) {
19 | throw 'tried to use expectObservable() in async test';
20 | }
21 | return global.rxTestScheduler.expectObservable.apply(global.rxTestScheduler, arguments);
22 | }
23 |
24 | function expectSubscriptions() {
25 | if (!global.rxTestScheduler) {
26 | throw 'tried to use expectSubscriptions() in async test';
27 | }
28 | return global.rxTestScheduler.expectSubscriptions.apply(global.rxTestScheduler, arguments);
29 | }
30 |
31 | function assertDeepEqual(actual, expected) {
32 | ( expect(actual)).toDeepEqual(expected);
33 | }
34 |
35 | export default {
36 | hot: hot,
37 | cold: cold,
38 | expectObservable: expectObservable,
39 | expectSubscriptions: expectSubscriptions,
40 | assertDeepEqual: assertDeepEqual
41 | };
--------------------------------------------------------------------------------
/operators/specs/helpers/test-helper.ts:
--------------------------------------------------------------------------------
1 | declare var global, require, beforeEach, afterEach, jasmine, Symbol;
2 |
3 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
4 |
5 | const isEqual = require('lodash.isequal');
6 |
7 | const root = require('rxjs/util/root').root;
8 | import {TestScheduler} from 'rxjs/testing/TestScheduler';
9 |
10 | import marbleHelpers from './marble-testing';
11 |
12 | global.rxTestScheduler = null;
13 | global.cold = marbleHelpers.cold;
14 | global.hot = marbleHelpers.hot;
15 | global.expectObservable = marbleHelpers.expectObservable;
16 | global.expectSubscriptions = marbleHelpers.expectSubscriptions;
17 |
18 | const assertDeepEqual = marbleHelpers.assertDeepEqual;
19 |
20 | const glit = global.it;
21 |
22 | global.it = function(description, cb, timeout) {
23 | if (cb.length === 0) {
24 | glit(description, function() {
25 | global.rxTestScheduler = new TestScheduler(assertDeepEqual);
26 | cb();
27 | global.rxTestScheduler.flush();
28 | });
29 | } else {
30 | glit.apply(this, arguments);
31 | }
32 | };
33 |
34 | global.it.asDiagram = function() {
35 | return global.it;
36 | };
37 |
38 | const glfit = global.fit;
39 |
40 | global.fit = function(description, cb, timeout) {
41 | if (cb.length === 0) {
42 | glfit(description, function() {
43 | global.rxTestScheduler = new TestScheduler(assertDeepEqual);
44 | cb();
45 | global.rxTestScheduler.flush();
46 | });
47 | } else {
48 | glfit.apply(this, arguments);
49 | }
50 | };
51 |
52 | function stringify(x) {
53 | return JSON.stringify(x, function(key, value) {
54 | if (Array.isArray(value)) {
55 | return '[' + value
56 | .map(function(i) {
57 | return '\n\t' + stringify(i);
58 | }) + '\n]';
59 | }
60 | return value;
61 | })
62 | .replace(/\\"/g, '"')
63 | .replace(/\\t/g, '\t')
64 | .replace(/\\n/g, '\n');
65 | }
66 |
67 | beforeEach(function() {
68 | jasmine.addMatchers({
69 | toDeepEqual: function(util, customEqualityTesters) {
70 | return {
71 | compare: function(actual, expected) {
72 | let result: any = { pass: isEqual(actual, expected) };
73 |
74 | if (!result.pass && Array.isArray(actual) && Array.isArray(expected)) {
75 | result.message = 'Expected \n';
76 | actual.forEach(function(x) {
77 | result.message += stringify(x) + '\n';
78 | });
79 | result.message += '\nto deep equal \n';
80 | expected.forEach(function(x) {
81 | result.message += stringify(x) + '\n';
82 | });
83 | }
84 |
85 | return result;
86 | }
87 | };
88 | }
89 | });
90 | });
91 |
92 | afterEach(function() {
93 | global.rxTestScheduler = null;
94 | });
95 |
96 | (function() {
97 | Object.defineProperty(Error.prototype, 'toJSON', {
98 | value: function() {
99 | let alt = {};
100 |
101 | Object.getOwnPropertyNames(this).forEach(function(key) {
102 | if (key !== 'stack') {
103 | alt[key] = this[key];
104 | }
105 | }, this);
106 | return alt;
107 | },
108 | configurable: true
109 | });
110 |
111 | global.__root__ = root;
112 | })();
113 |
114 | global.lowerCaseO = function lowerCaseO() {
115 | const values = [].slice.apply(arguments);
116 |
117 | const o = {
118 | subscribe: function(observer) {
119 | values.forEach(function(v) {
120 | observer.next(v);
121 | });
122 | observer.complete();
123 | }
124 | };
125 |
126 | o[(Symbol).observable] = function() {
127 | return this;
128 | };
129 |
130 | return o;
131 | };
--------------------------------------------------------------------------------
/operators/transformation/README.md:
--------------------------------------------------------------------------------
1 | # 转换操作符
2 |
3 | 在通过操作符链时进行值的转换是一个常见的任务。这些操作符提供了转换技术几乎可以涵盖你所能遇到的任何场景。
4 |
5 | ## 内容
6 |
7 | * [buffer](buffer.md)
8 | * [bufferCount](buffercount.md)
9 | * [bufferTime](buffertime.md) :star:
10 | * [bufferToggle](buffertoggle.md)
11 | * [bufferWhen](bufferwhen.md)
12 | * [concatMap](concatmap.md) :star:
13 | * [concatMapTo](concatmapto.md)
14 | * [exhaustMap](exhaustmap.md)
15 | * [expand](expand.md)
16 | * [groupBy](groupby.md)
17 | * [map](map.md) :star:
18 | * [mapTo](mapto.md)
19 | * [mergeMap / flatMap](mergemap.md) :star:
20 | * [partition](partition.md)
21 | * [pluck](pluck.md)
22 | * [reduce](reduce.md)
23 | * [scan](scan.md) :star:
24 | * [switchMap](switchmap.md) :star:
25 | * [window](window.md)
26 | * [windowCount](windowcount.md)
27 | * [windowTime](windowtime.md)
28 | * [windowToggle](windowtoggle.md)
29 | * [windowWhen](windowwhen.md)
30 |
31 | :star: - *常用*
32 |
--------------------------------------------------------------------------------
/operators/transformation/buffer.md:
--------------------------------------------------------------------------------
1 | # buffer
2 |
3 | #### 函数签名: `buffer(closingNotifier: Observable): Observable`
4 |
5 | ## 收集输出值,直到提供的 observable 发出才将收集到的值作为数组发出。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 缓冲值直到点击页面
12 |
13 | ( [StackBlitz](https://stackblitz.com/edit/typescript-nwp2cl?file=index.ts&devtoolsheight=50) |
14 | [jsBin](http://jsbin.com/fazimarajo/edit?js,console,output) |
15 | [jsFiddle](https://jsfiddle.net/btroncone/7451s67k/) )
16 |
17 | ```js
18 | // RxJS v6+
19 | import { interval, fromEvent } from 'rxjs';
20 | import { buffer } from 'rxjs/operators';
21 |
22 | // 创建每1秒发出值的 observable
23 | const myInterval = interval(1000);
24 | // 创建页面点击事件的 observable
25 | const bufferBy = fromEvent(document, 'click');
26 | /*
27 | 收集由 myInterval 发出的所有值,直到我们点击页面。此时 bufferBy 会发出值以完成缓存。
28 | 将自上次缓冲以来收集的所有值传递给数组。
29 | */
30 | const myBufferedInterval = myInterval.pipe(buffer(bufferBy));
31 | // 打印值到控制台
32 | // 例如 输出: [1,2,3] ... [4,5,6,7,8]
33 | const subscribe = myBufferedInterval.subscribe(val =>
34 | console.log(' Buffered Values:', val)
35 | );
36 | ```
37 |
38 | ### 相关食谱
39 |
40 | * [游戏循环](../../recipes/gameloop.md)
41 |
42 | ### 其他资源
43 |
44 | * [buffer](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-buffer) :newspaper: - 官方文档
45 | * [转换操作符: buffer](https://egghead.io/lessons/rxjs-transformation-operator-buffer?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
46 |
47 | ---
48 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/buffer.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/buffer.ts)
49 |
--------------------------------------------------------------------------------
/operators/transformation/buffercount.md:
--------------------------------------------------------------------------------
1 | # bufferCount
2 |
3 | #### 函数签名: `bufferCount(bufferSize: number, startBufferEvery: number = null): Observable`
4 |
5 | ## 收集发出的值,直到收集完提供的数量的值才将其作为数组发出。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 收集缓冲区并在指定数量的值后发出
12 |
13 | ( [StackBlitz](https://stackblitz.com/edit/typescript-osryhu?file=index.ts&devtoolsheight=50) |
14 | [jsBin](http://jsbin.com/suveqaromu/1/edit?js,console) |
15 | [jsFiddle](https://jsfiddle.net/btroncone/ky9myc5b/) )
16 |
17 | ```js
18 | // RxJS v6+
19 | import { interval } from 'rxjs';
20 | import { bufferCount } from 'rxjs/operators';
21 |
22 | // 创建每1秒发出值的 observable
23 | const source = interval(1000);
24 | // 在发出3个值后,将缓冲的值作为数组传递
25 | const bufferThree = source.pipe(bufferCount(3));
26 | // 打印值到控制台
27 | // 输出: [0,1,2]...[3,4,5]
28 | const subscribe = bufferThree.subscribe(val =>
29 | console.log('Buffered Values:', val)
30 | );
31 | ```
32 |
33 | ##### 示例 2: 重叠的缓冲
34 |
35 | ( [StackBlitz](https://stackblitz.com/edit/typescript-vvccar?file=index.ts&devtoolsheight=100) |
36 | [jsBin](http://jsbin.com/kiloxiraya/1/edit?js,console) |
37 | [jsFiddle](https://jsfiddle.net/btroncone/3c67qcz1/) )
38 |
39 | ```js
40 | // RxJS v6+
41 | import { interval } from 'rxjs';
42 | import { bufferCount } from 'rxjs/operators';
43 |
44 | // 创建每1秒发出值的 observable
45 | const source = interval(1000);
46 | /*
47 | bufferCount 还接受第二个参数,何时开启下一个缓冲区
48 | 举例来说,如果第一个参数(bufferSize)是3,而第二个参数(startBufferEvery)是1:
49 | 第一次 interval 的值:
50 | buffer 1: [0]
51 | 第2次 interval 的值:
52 | buffer 1: [0,1]
53 | buffer 2: [1]
54 | 第3次 interval 的值:
55 | buffer 1: [0,1,2] 缓冲数量已达到3,发出缓冲区
56 | buffer 2: [1,2]
57 | buffer 3: [2]
58 | 第4次 interval 的值:
59 | buffer 2: [1,2,3] 缓冲数量已达到3,发出缓冲区
60 | buffer 3: [2, 3]
61 | buffer 4: [3]
62 | */
63 | const bufferEveryOne = source.pipe(bufferCount(3, 1));
64 | // 打印值到控制台
65 | const subscribe = bufferEveryOne.subscribe(val =>
66 | console.log('Start Buffer Every 1:', val)
67 | );
68 | ```
69 |
70 | ### 其他资源
71 |
72 | * [bufferCount](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-bufferCount) :newspaper: - 官方文档
73 |
74 | ---
75 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/bufferCount.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/bufferCount.ts)
76 |
--------------------------------------------------------------------------------
/operators/transformation/buffertime.md:
--------------------------------------------------------------------------------
1 | # bufferTime
2 |
3 | #### 函数签名: `bufferTime(bufferTimeSpan: number, bufferCreationInterval: number, scheduler: Scheduler): Observable`
4 |
5 | ## 收集发出的值,直到经过了提供的时间才将其作为数组发出。
6 |
7 | ### 示例
8 |
9 | ##### 示例 1: 缓冲2秒
10 |
11 | ( [StackBlitz](https://stackblitz.com/edit/typescript-haqxd1?file=index.ts&devtoolsheight=50) | [jsBin](http://jsbin.com/bafakiyife/1/edit?js,console) |
12 | [jsFiddle](https://jsfiddle.net/btroncone/vx7vwg01/) )
13 |
14 | ```js
15 | // RxJS v6+
16 | import { interval } from 'rxjs';
17 | import { bufferTime } from 'rxjs/operators';
18 |
19 | // 创建每500毫秒发出值的 observable
20 | const source = interval(500);
21 | // 2秒后,将缓冲值作为数组发出
22 | const example = source.pipe(bufferTime(2000));
23 | // 打印值到控制台
24 | // 输出: [0,1,2]...[3,4,5,6]
25 | const subscribe = example.subscribe(val =>
26 | console.log('Buffered with Time:', val)
27 | );
28 | ```
29 |
30 | ##### 示例 2: 多个有效的缓冲区
31 |
32 | ( [StackBlitz](https://stackblitz.com/edit/typescript-9blquz?file=index.ts&devtoolsheight=100) | [jsBin](http://jsbin.com/tadiwiniri/1/edit?js,console) |
33 | [jsFiddle](https://jsfiddle.net/btroncone/7k4ygj1x/) )
34 |
35 | ```js
36 | // RxJS v6+
37 | import { interval } from 'rxjs';
38 | import { bufferTime } from 'rxjs/operators';
39 |
40 | // 创建每500毫秒发出值的 observable
41 | const source = interval(500);
42 | /*
43 | bufferTime 还接受第二个参数,何时开始下一个缓冲区(时间单位为毫秒)
44 | 举例来说,如果第一个参数(bufferTimeSpan)是2秒,而第二个参数(bufferCreationInterval)是1秒:
45 | */
46 | const example = source.pipe(bufferTime(2000, 1000));
47 | // 打印值到控制台
48 | const subscribe = example.subscribe(val =>
49 | console.log('Start Buffer Every 1s:', val)
50 | );
51 | ```
52 |
53 | ### 其他资源
54 |
55 | - [bufferTime](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-bufferTime) :newspaper: - 官方文档
56 |
57 | ---
58 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/bufferTime.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/bufferTime.ts)
59 |
--------------------------------------------------------------------------------
/operators/transformation/buffertoggle.md:
--------------------------------------------------------------------------------
1 | # bufferToggle
2 |
3 | #### 函数签名: `bufferToggle(openings: Observable, closingSelector: Function): Observable`
4 |
5 | ## 开启开关以捕获源 observable 所发出的值,关闭开关以将缓冲的值作为数组发出。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 使用 interval 来切换缓冲的开关
12 |
13 | ( [StackBlitz](https://stackblitz.com/edit/typescript-xu3sq8?file=index.ts&devtoolsheight=100) | [jsBin](http://jsbin.com/relavezugo/edit?js,console) |
14 | [jsFiddle](https://jsfiddle.net/btroncone/6ad3w3wf/) )
15 |
16 | ```js
17 | // RxJS v6+
18 | import { interval } from 'rxjs';
19 | import { bufferToggle } from 'rxjs/operators';
20 |
21 | // 每1秒发出值
22 | const sourceInterval = interval(1000);
23 | // 5秒后开启第一个缓冲区,然后每5秒钟开启新的缓冲区
24 | const startInterval = interval(5000);
25 | // 3秒后发出值以关闭相应的缓冲区
26 | const closingInterval = val => {
27 | console.log(`Value ${val} emitted, starting buffer! Closing in 3s!`);
28 | return interval(3000);
29 | };
30 | // 每5秒会开启一个新的缓冲区以收集发出的值,3秒后发出缓冲的值
31 | const bufferToggleInterval = sourceInterval.pipe(
32 | bufferToggle(
33 | startInterval,
34 | closingInterval
35 | )
36 | );
37 | // 输出到控制台
38 | // 输出: Emitted Buffer: [4,5,6]...[9,10,11]
39 | const subscribe = bufferToggleInterval.subscribe(val =>
40 | console.log('Emitted Buffer:', val)
41 | );
42 | ```
43 |
44 | ### 其他资源
45 |
46 | * [bufferToggle](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-bufferToggle) :newspaper: - 官方文档
47 |
48 | ---
49 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/bufferToggle.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/bufferToggle.ts)
50 |
--------------------------------------------------------------------------------
/operators/transformation/bufferwhen.md:
--------------------------------------------------------------------------------
1 | # bufferWhen
2 |
3 | #### 函数签名: `bufferWhen(closingSelector: function): Observable`
4 |
5 | ## 收集值,直到关闭选择器发出值才发出缓冲的值。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 发出基于 interval 缓冲的值
12 |
13 | ( [StackBlitz](https://stackblitz.com/edit/typescript-f4a2fu?file=index.ts&devtoolsheight=10) | [jsBin](http://jsbin.com/vugerupube/1/edit?js,console) |
14 | [jsFiddle](https://jsfiddle.net/btroncone/nr9agfuL/) )
15 |
16 | ```js
17 | // RxJS v6+
18 | import { interval } from 'rxjs';
19 | import { bufferWhen } from 'rxjs/operators';
20 |
21 | // 每1秒发出值
22 | const oneSecondInterval = interval(1000);
23 | // 返回的 observable 每5秒发出值
24 | const fiveSecondInterval = () => interval(5000);
25 | // 每5秒发出缓冲的值
26 | const bufferWhenExample = oneSecondInterval.pipe(bufferWhen(fiveSecondInterval));
27 | // 输出值
28 | // 输出: [0,1,2,3]...[4,5,6,7,8]
29 | const subscribe = bufferWhenExample.subscribe(val =>
30 | console.log('Emitted Buffer: ', val)
31 | );
32 | ```
33 |
34 | ### 其他资源
35 |
36 | * [bufferWhen](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-bufferWhen) :newspaper: - 官方文档
37 |
38 | ---
39 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/bufferWhen.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/bufferWhen.ts)
40 |
--------------------------------------------------------------------------------
/operators/transformation/concatmap.md:
--------------------------------------------------------------------------------
1 | # concatMap
2 |
3 | #### 函数签名: `concatMap(project: function, resultSelector: function): Observable`
4 |
5 | ## 将值映射成内部 observable,并按顺序订阅和发出。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 演示 `concatMap` 和 [`mergeMap`](./mergemap.md) 之间的区别
12 |
13 | ( [StackBlitz](https://stackblitz.com/edit/typescript-pkyxa1?file=index.ts&devtoolsheight=100) )
14 |
15 | :bulb: 注意 `concatMap` 和 [`mergeMap`](./mergemap.md) 之间的区别。
16 | 因为 `concatMap` 之前前一个内部 observable 完成后才会订阅下一个,
17 | source 中延迟 2000ms 值会先发出。
18 | 对比的话, [`mergeMap`](./mergemap.md) 会立即订阅所有内部 observables,
19 | 延迟少的 observable (1000ms) 会先发出值,然后才是 2000ms 的 observable 。
20 |
21 | ```js
22 | // RxJS v6+
23 | import { of } from 'rxjs';
24 | import { concatMap, delay, mergeMap } from 'rxjs/operators';
25 |
26 | // 发出延迟值
27 | const source = of(2000, 1000);
28 | // 将内部 observable 映射成 source,当前一个完成时发出结果并订阅下一个
29 | const example = source.pipe(
30 | concatMap(val => of(`Delayed by: ${val}ms`).pipe(delay(val)))
31 | );
32 | // 输出: With concatMap: Delayed by: 2000ms, With concatMap: Delayed by: 1000ms
33 | const subscribe = example.subscribe(val =>
34 | console.log(`With concatMap: ${val}`)
35 | );
36 |
37 | // 展示 concatMap 和 mergeMap 之间的区别
38 | const mergeMapExample = source
39 | .pipe(
40 | // 只是为了确保 meregeMap 的日志晚于 concatMap 示例
41 | delay(5000),
42 | mergeMap(val => of(`Delayed by: ${val}ms`).pipe(delay(val)))
43 | )
44 | .subscribe(val => console.log(`With mergeMap: ${val}`));
45 | ```
46 |
47 | ##### 示例 2: 映射成 promise
48 |
49 | ( [StackBlitz](https://stackblitz.com/edit/typescript-rv9byk?file=index.ts&devtoolsheight=100) |
50 | [jsBin](http://jsbin.com/celixodeba/1/edit?js,console) |
51 | [jsFiddle](https://jsfiddle.net/btroncone/Lym33L97//) )
52 |
53 | ```js
54 | // RxJS v6+
55 | import { of } from 'rxjs';
56 | import { concatMap } from 'rxjs/operators';
57 |
58 | // 发出 'Hello' 和 'Goodbye'
59 | const source = of('Hello', 'Goodbye');
60 | // 使用 promise 的示例
61 | const examplePromise = val => new Promise(resolve => resolve(`${val} World!`));
62 | // 将 source 的值映射成内部 observable,当一个完成发出结果后再继续下一个
63 | const example = source.pipe(concatMap(val => examplePromise(val)));
64 | // 输出: 'Example w/ Promise: 'Hello World', Example w/ Promise: 'Goodbye World'
65 | const subscribe = example.subscribe(val =>
66 | console.log('Example w/ Promise:', val)
67 | );
68 | ```
69 |
70 | ##### 示例 3: 应用投射函数
71 |
72 | ( [StackBlitz](https://stackblitz.com/edit/typescript-2elzt7?file=index.ts&devtoolsheight=100) |
73 | [jsBin](http://jsbin.com/vihacewozo/1/edit?js,console) |
74 | [jsFiddle](https://jsfiddle.net/btroncone/5sr5zzgy/) )
75 |
76 | ```js
77 | // RxJS v6+
78 | import { of } from 'rxjs';
79 | import { concatMap } from 'rxjs/operators';
80 |
81 | // 发出 'Hello' 和 'Goodbye'
82 | const source = of('Hello', 'Goodbye');
83 | // 使用 promise 的示例
84 | const examplePromise = val => new Promise(resolve => resolve(`${val} World!`));
85 | // 返回结果前,第一个参数的结果将传递给第二个参数选择器函数
86 | const example = source.pipe(
87 | concatMap(val => examplePromise(val), result => `${result} w/ selector!`)
88 | );
89 | // 输出: 'Example w/ Selector: 'Hello w/ Selector', Example w/ Selector: 'Goodbye w/ Selector'
90 | const subscribe = example.subscribe(val =>
91 | console.log('Example w/ Selector:', val)
92 | );
93 | ```
94 |
95 | ### 其他资源
96 |
97 | * [concatMap](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-concatMap) :newspaper: - 官方文档
98 | * [使用 RxJS 的 concatMap 操作符来映射并连接高阶 observables](https://egghead.io/lessons/rxjs-use-rxjs-concatmap-to-map-and-concat-high-order-observables?course=use-higher-order-observables-in-rxjs-effectively) :video_camera: :dollar: - André Staltz
99 |
100 | ---
101 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/concatMap.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/concatMap.ts)
102 |
--------------------------------------------------------------------------------
/operators/transformation/concatmapto.md:
--------------------------------------------------------------------------------
1 | # concatMapTo
2 |
3 | #### 函数签名: `concatMapTo(observable: Observable, resultSelector: function): Observable`
4 |
5 | ## 当前一个 observable 完成时订阅提供的 observable 并发出值。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 映射成基础的 observable (模拟请求)
12 |
13 | ( [StackBlitz](https://stackblitz.com/edit/typescript-fkkh6c?file=index.ts&devtoolsheight=50) )
14 |
15 | ```js
16 | // RxJS v6+
17 | import { of, interval } from 'rxjs';
18 | import { concatMapTo, delay, take } from 'rxjs/operators';
19 |
20 | // 每2秒发出值
21 | const sampleInterval = interval(500).pipe(take(5));
22 | const fakeRequest = of('Network request complete').pipe(delay(3000));
23 | // 前一个完成才会订阅下一个
24 | const example = sampleInterval.pipe(concatMapTo(fakeRequest));
25 | // 结果
26 | // 输出: Network request complete...3s...Network request complete'
27 | const subscribe = example.subscribe(val => console.log(val));
28 | ```
29 |
30 | ##### 示例 2: 使用 `concatMap` 的投射函数
31 |
32 | ( [StackBlitz](https://stackblitz.com/edit/typescript-8kcfm1?file=index.ts&devtoolsheight=100) |
33 | [jsBin](http://jsbin.com/fogefebisu/1/edit?js,console) |
34 | [jsFiddle](https://jsfiddle.net/btroncone/s19wtscb/) )
35 |
36 | ```js
37 | // RxJS v6+
38 | import { interval } from 'rxjs';
39 | import { concatMapTo, take } from 'rxjs/operators';
40 | // 每2秒发出值
41 | const interval$ = interval(2000);
42 | // 每1秒发出值,共5秒
43 | const source = interval(1000).pipe(take(5));
44 | /*
45 | ***小心***: 像这种情况下,源 observable 以比内部 observable 完成速度更快的速度发出,内存问题可能会出现。
46 | (interval 每1秒发出值,source 每5秒钟完成)
47 | */
48 | // ource 会在5秒后完成, 发出 0,1,2,3,4
49 | const example = interval$.pipe(
50 | concatMapTo(
51 | source,
52 | (firstInterval, secondInterval) => `${firstInterval} ${secondInterval}`
53 | )
54 | );
55 | /*
56 | 输出: 0 0
57 | 0 1
58 | 0 2
59 | 0 3
60 | 0 4
61 | 1 0
62 | 1 1
63 | 继续...
64 |
65 | */
66 | const subscribe = example.subscribe(val => console.log(val));
67 | ```
68 |
69 | ### 其他资源
70 |
71 | - [concatMapTo](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-concatMapTo) :newspaper: - 官方文档
72 |
73 | ---
74 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/concatMapTo.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/concatMapTo.ts)
75 |
--------------------------------------------------------------------------------
/operators/transformation/exhaustmap.md:
--------------------------------------------------------------------------------
1 | # exhaustMap
2 |
3 | #### 函数签名: `exhaustMap(project: function, resultSelector: function): Observable`
4 |
5 | ## 映射成内部 observable,忽略其他值直到该 observable 完成。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 使用 interval 的 exhaustMap
12 |
13 | ( [Stackblitz](https://stackblitz.com/edit/typescript-3qydhn?file=index.ts&devtoolsheight=100) |
14 | [jsBin](http://jsbin.com/woposeqobo/1/edit?js,console) |
15 | [jsFiddle](https://jsfiddle.net/btroncone/9ovzapp9/) )
16 |
17 | ```js
18 | // RxJS v6+
19 | import { interval, merge, of } from 'rxjs';
20 | import { delay, take, exhaustMap } from 'rxjs/operators';
21 |
22 | const sourceInterval = interval(1000);
23 | const delayedInterval = sourceInterval.pipe(delay(10), take(4));
24 |
25 | const exhaustSub = merge(
26 | // 延迟10毫秒,然后开始 interval 并发出4个值
27 | delayedInterval,
28 | // 立即发出
29 | of(true)
30 | )
31 | .pipe(exhaustMap(_ => sourceInterval.pipe(take(5))))
32 | /*
33 | * 第一个发出的值 (of(true)) 会被映射成每秒发出值、
34 | * 5秒后完成的 interval observable 。
35 | * 因为 delayedInterval 的发送是晚于前者的,虽然 observable
36 | * 仍然是活动的,但它们会被忽略。
37 | *
38 | * 与类似的操作符进行下对比:
39 | * concatMap 会进行排队
40 | * switchMap 会在每次发送时切换成新的内部 observable
41 | * mergeMap 会为每个发出值维护新的 subscription
42 | */
43 | // 输出: 0, 1, 2, 3, 4
44 | .subscribe(val => console.log(val));
45 | ```
46 |
47 | ##### 示例 2: 另一个使用 interval 的 exhaustMap
48 |
49 | ( [Stackblitz](https://stackblitz.com/edit/typescript-vxussb?file=index.ts&devtoolsheight=100) |
50 | [jsBin](http://jsbin.com/fizuduzuti/1/edit?js,console) |
51 | [jsFiddle](https://jsfiddle.net/btroncone/5ck8yg5k/3/) )
52 |
53 | ```js
54 | // RxJS v6+
55 | import { interval } from 'rxjs';
56 | import { exhaustMap, tap, take } from 'rxjs/operators';
57 |
58 | const firstInterval = interval(1000).pipe(take(10));
59 | const secondInterval = interval(1000).pipe(take(2));
60 |
61 | const exhaustSub = firstInterval
62 | .pipe(
63 | exhaustMap(f => {
64 | console.log(`Emission Corrected of first interval: ${f}`);
65 | return secondInterval;
66 | })
67 | )
68 | /*
69 | 当我们订阅第一个 interval 时,它开始发出值(从0开始)。
70 | 这个值会映射成第二个 interval,然后它开始发出值(从0开始)。
71 | 当第二个 interval 出于激活状态时,第一个 interval 的值会被忽略。
72 | 我们可以看到 firstInterval 发出的数字为3,6,等等...
73 |
74 | 输出:
75 | Emission of first interval: 0
76 | 0
77 | 1
78 | Emission of first interval: 3
79 | 0
80 | 1
81 | Emission of first interval: 6
82 | 0
83 | 1
84 | Emission of first interval: 9
85 | 0
86 | 1
87 | */
88 | .subscribe(s => console.log(s));
89 | ```
90 |
91 | ### 外部示例
92 |
93 | ##### `exhaustMap` 用于 [@ngrx 示例应用](https://github.com/ngrx/platform/tree/a9e522953832b09bb329bac4524637bc608c450a/example-app) 的 login Effect
94 |
95 | (
96 | [Source](https://github.com/ngrx/platform/blob/a9e522953832b09bb329bac4524637bc608c450a/example-app/app/auth/effects/auth.effects.ts#L18-L30)
97 | )
98 |
99 | ```js
100 | @Effect()
101 | login$ = this.actions$.pipe(
102 | ofType(AuthActionTypes.Login),
103 | map((action: Login) => action.payload),
104 | exhaustMap((auth: Authenticate) =>
105 | this.authService
106 | .login(auth)
107 | .pipe(
108 | map(user => new LoginSuccess({ user })),
109 | catchError(error => of(new LoginFailure(error)))
110 | )
111 | )
112 | );
113 | ```
114 |
115 | ### 其他资源
116 |
117 | * [exhaustMap](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-exhaustMap) :newspaper: - 官方文档
118 |
119 | ---
120 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/exhaustMap.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/exhaustMap.ts)
121 |
--------------------------------------------------------------------------------
/operators/transformation/expand.md:
--------------------------------------------------------------------------------
1 | # expand
2 |
3 | #### 函数签名: `expand(project: function, concurrent: number, scheduler: Scheduler): Observable`
4 |
5 | ## 递归调用提供的函数
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 每次调用加1
12 |
13 | ( [StackBlitz](https://stackblitz.com/edit/typescript-ntgecj?file=index.ts&devtoolsheight=100) |
14 | [jsBin](http://jsbin.com/fuxocepazi/1/edit?js,console) |
15 | [jsFiddle](https://jsfiddle.net/btroncone/nu4apbLt/) )
16 |
17 | ```js
18 | // RxJS v6+
19 | import { interval, of } from 'rxjs';
20 | import { expand, take } from 'rxjs/operators';
21 |
22 | // 发出 2
23 | const source = of(2);
24 | const example = source.pipe(
25 | // 递归调用提供的函数
26 | expand(val => {
27 | // 2,3,4,5,6
28 | console.log(`Passed value: ${val}`);
29 | // 3,4,5,6
30 | return of(1 + val);
31 | }),
32 | // 用5次
33 | take(5)
34 | );
35 | /*
36 | "RESULT: 2"
37 | "Passed value: 2"
38 | "RESULT: 3"
39 | "Passed value: 3"
40 | "RESULT: 4"
41 | "Passed value: 4"
42 | "RESULT: 5"
43 | "Passed value: 5"
44 | "RESULT: 6"
45 | "Passed value: 6"
46 | */
47 | // 输出: 2,3,4,5,6
48 | const subscribe = example.subscribe(val => console.log(`RESULT: ${val}`));
49 | ```
50 |
51 | ### 相关食谱
52 |
53 | * [游戏循环](../../recipes/gameloop.md)
54 |
55 | ### 其他资源
56 |
57 | * [expand](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-expand) :newspaper: - 官方文档
58 |
59 | ---
60 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/expand.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/expand.ts)
61 |
--------------------------------------------------------------------------------
/operators/transformation/groupby.md:
--------------------------------------------------------------------------------
1 | # groupBy
2 |
3 | #### 函数签名: `groupBy(keySelector: Function, elementSelector: Function): Observable`
4 |
5 | ## 基于提供的值分组成多个 observables
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 根据属性分组
12 |
13 | ( [StackBlitz](https://stackblitz.com/edit/typescript-dozkcg?file=index.ts&devtoolsheight=100) |
14 | [jsBin](http://jsbin.com/buworowuye/edit?js,console) |
15 | [jsFiddle](https://jsfiddle.net/btroncone/utncxxvf/) )
16 |
17 | ```js
18 | // RxJS v6+
19 | import { from } from 'rxjs';
20 | import { groupBy, mergeMap, toArray } from 'rxjs/operators';
21 |
22 | const people = [
23 | { name: 'Sue', age: 25 },
24 | { name: 'Joe', age: 30 },
25 | { name: 'Frank', age: 25 },
26 | { name: 'Sarah', age: 35 }
27 | ];
28 | // 发出每个 people
29 | const source = from(people);
30 | // 根据 age 分组
31 | const example = source.pipe(
32 | groupBy(person => person.age),
33 | // 为每个分组返回一个数组
34 | mergeMap(group => group.pipe(toArray()))
35 | );
36 | /*
37 | 输出:
38 | [{age: 25, name: "Sue"},{age: 25, name: "Frank"}]
39 | [{age: 30, name: "Joe"}]
40 | [{age: 35, name: "Sarah"}]
41 | */
42 | const subscribe = example.subscribe(val => console.log(val));
43 | ```
44 |
45 | ### 其他资源
46 |
47 | * [groupBy](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-groupBy) :newspaper: - 官方文档
48 | * [使用 RxJS 的 groupBy 操作符分组成高阶 observables](https://egghead.io/lessons/rxjs-group-higher-order-observables-with-rxjs-groupby?course=use-higher-order-observables-in-rxjs-effectively) :video_camera: :dollar: - André Staltz
49 | * [在真实的 RxJS 应用中使用 groupBy](https://egghead.io/lessons/rxjs-use-groupby-in-real-rxjs-applications?course=use-higher-order-observables-in-rxjs-effectively) :video_camera: :dollar: - André Staltz
50 |
51 | ---
52 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/groupBy.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/groupBy.ts)
53 |
--------------------------------------------------------------------------------
/operators/transformation/map.md:
--------------------------------------------------------------------------------
1 | # map
2 |
3 | #### 函数签名: `map(project: Function, thisArg: any): Observable`
4 |
5 | ## 对源 observable 的每个值应用投射函数。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 每个数字加10
12 |
13 | ( [StackBlitz](https://stackblitz.com/edit/typescript-a7bnxb?file=index.ts&devtoolsheight=100) |
14 | [jsBin](http://jsbin.com/padasukano/1/edit?js,console) |
15 | [jsFiddle](https://jsfiddle.net/btroncone/yd38awLa/) )
16 |
17 | ```js
18 | // RxJS v6+
19 | import { from } from 'rxjs';
20 | import { map } from 'rxjs/operators';
21 |
22 | // 发出 (1,2,3,4,5)
23 | const source = from([1, 2, 3, 4, 5]);
24 | // 每个数字加10
25 | const example = source.pipe(map(val => val + 10));
26 | // 输出: 11,12,13,14,15
27 | const subscribe = example.subscribe(val => console.log(val));
28 | ```
29 |
30 | ##### 示例 2: 映射成单一属性
31 |
32 | ( [StackBlitz](https://stackblitz.com/edit/typescript-qgpnju?file=index.ts&devtoolsheight=100) |
33 | [jsBin](http://jsbin.com/detozumale/1/edit?js,console) |
34 | [jsFiddle](https://jsfiddle.net/btroncone/tdLd5tgc/) )
35 |
36 | ```js
37 | // RxJS v6+
38 | import { from } from 'rxjs';
39 | import { map } from 'rxjs/operators';
40 |
41 | // 发出 ({name: 'Joe', age: 30}, {name: 'Frank', age: 20},{name: 'Ryan', age: 50})
42 | const source = from([
43 | { name: 'Joe', age: 30 },
44 | { name: 'Frank', age: 20 },
45 | { name: 'Ryan', age: 50 }
46 | ]);
47 | // 提取每个 person 的 name 属性
48 | const example = source.pipe(map(({ name }) => name));
49 | // 输出: "Joe","Frank","Ryan"
50 | const subscribe = example.subscribe(val => console.log(val));
51 | ```
52 |
53 | ### 相关食谱
54 |
55 | * [智能计数器](../../recipes/smartcounter.md)
56 | * [游戏循环](../../recipes/gameloop.md)
57 | * [HTTP 轮询](../../recipes/http-polling.md)
58 |
59 | ### 其他资源
60 |
61 | * [map](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-map) :newspaper: - 官方文档
62 | * [map vs flatMap](https://egghead.io/lessons/rxjs-rxjs-map-vs-flatmap) :video_camera: - Ben Lesh
63 | * [转换操作符: map 和 mapTo](https://egghead.io/lessons/rxjs-transformation-operator-map-and-mapto?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
64 |
65 | ---
66 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/map.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/map.ts)
67 |
--------------------------------------------------------------------------------
/operators/transformation/mapto.md:
--------------------------------------------------------------------------------
1 | # mapTo
2 |
3 | #### 函数签名: `mapTo(value: any): Observable`
4 |
5 | ## 将每个发出值映射成常量。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 将每个发出值映射成字符串
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-fipd7a?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/qujolenili/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/4ojq56ng/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { interval } from 'rxjs';
21 | import { mapTo } from 'rxjs/operators';
22 |
23 | // 每2秒发出值
24 | const source = interval(2000);
25 | // 将所有发出值映射成同一个值
26 | const example = source.pipe(mapTo('HELLO WORLD!'));
27 | // 输出: 'HELLO WORLD!'...'HELLO WORLD!'...'HELLO WORLD!'...
28 | const subscribe = example.subscribe(val => console.log(val));
29 | ```
30 |
31 | ##### 示例 2: 将点击映射成字符串
32 |
33 | (
34 | [StackBlitz](https://stackblitz.com/edit/typescript-btghci?file=index.ts&devtoolsheight=100)
35 | | [jsBin](http://jsbin.com/xaheciwara/1/edit?js,console,output) |
36 | [jsFiddle](https://jsfiddle.net/btroncone/52fqL4nn/) )
37 |
38 | ```js
39 | // RxJS v6+
40 | import { fromEvent } from 'rxjs';
41 | import { mapTo } from 'rxjs/operators';
42 |
43 | // 发出每个页面点击
44 | const source = fromEvent(document, 'click');
45 | // 将所有发出值映射成同一个值
46 | const example = source.pipe(mapTo('GOODBYE WORLD!'));
47 | // 输出: (click)'GOODBYE WORLD!'...
48 | const subscribe = example.subscribe(val => console.log(val));
49 | ```
50 |
51 | ### 相关食谱
52 |
53 | * [HTTP 轮询](../../recipes/http-polling.md)
54 | * [智能计数器](../../recipes/smartcounter.md)
55 |
56 | ### 其他资源
57 |
58 | * [mapTo](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-mapTo) :newspaper: - 官方文档
59 | * [使用 mapTo 来改变行为](https://egghead.io/lessons/rxjs-changing-behavior-with-mapto?course=step-by-step-async-javascript-with-rxjs) :video_camera: :dollar: - John Linquist
60 | * [转换操作符: map 和 mapTo](https://egghead.io/lessons/rxjs-transformation-operator-map-and-mapto?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
61 |
62 | ---
63 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/mapTo.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/mapTo.ts)
64 |
--------------------------------------------------------------------------------
/operators/transformation/partition.md:
--------------------------------------------------------------------------------
1 | # partition
2 |
3 | #### 函数签名: `partition(predicate: function: boolean, thisArg: any): [Observable, Observable]`
4 |
5 | ## Split one observable into two based on provided predicate.
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 分割偶数和奇数
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-gr3ljs?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/hipehexaku/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/q0xo7gvv/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { from, merge } from 'rxjs';
21 | import { partition, map } from 'rxjs/operators';
22 |
23 | const source = from([1, 2, 3, 4, 5, 6]);
24 | // 第一个值(events)返回 true 的数字集合,第二个值(odds)是返回 false 的数字集合
25 | const [evens, odds] = source.pipe(partition(val => val % 2 === 0));
26 | /*
27 | 输出:
28 | "Even: 2"
29 | "Even: 4"
30 | "Even: 6"
31 | "Odd: 1"
32 | "Odd: 3"
33 | "Odd: 5"
34 | */
35 | const subscribe = merge(
36 | evens.pipe(map(val => `Even: ${val}`)),
37 | odds.pipe(map(val => `Odd: ${val}`))
38 | ).subscribe(val => console.log(val));
39 | ```
40 |
41 | ##### 示例 2: 分割正常执行和错误
42 |
43 | (
44 | [StackBlitz](https://stackblitz.com/edit/typescript-vmfvp8?file=index.ts&devtoolsheight=100)
45 | | [jsBin](http://jsbin.com/kukuguhuri/1/edit?js,console) |
46 | [jsFiddle](https://jsfiddle.net/btroncone/fe246u5p/) )
47 |
48 | ```js
49 | // RxJS v6+
50 | import { merge, of, from } from 'rxjs';
51 | import { map, partition, catchError } from 'rxjs/operators';
52 |
53 | const source = from([1, 2, 3, 4, 5, 6]);
54 | // 如果大于3就抛出错误
55 | const example = source.pipe(
56 | map(val => {
57 | if (val > 3) {
58 | throw `${val} greater than 3!`;
59 | }
60 | return { success: val };
61 | }),
62 | catchError(val => of({ error: val }))
63 | );
64 | // 分割正常执行或错误
65 | const [success, error] = example.pipe(partition(res => res.success));
66 | /*
67 | 输出:
68 | "Success! 1"
69 | "Success! 2"
70 | "Success! 3"
71 | "Error! 4 greater than 3!"
72 | */
73 | const subscribe = merge(
74 | success.pipe(map(val => `Success! ${val.success}`)),
75 | error.pipe(map(val => `Error! ${val.error}`))
76 | ).subscribe(val => console.log(val));
77 | ```
78 |
79 | ### 其他资源
80 |
81 | - [partition](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-partition) :newspaper: - 官方文档
82 |
83 | ---
84 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/partition.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/partition.ts)
85 |
--------------------------------------------------------------------------------
/operators/transformation/pluck.md:
--------------------------------------------------------------------------------
1 | # pluck
2 |
3 | #### 函数签名: `pluck(properties: ...args): Observable`
4 |
5 | ## 选择属性来发出。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 提取对象属性
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-jkda4e?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/zokaxiwahe/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/58v9xq0f/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { from } from 'rxjs';
21 | import { pluck } from 'rxjs/operators';
22 |
23 | const source = from([{ name: 'Joe', age: 30 }, { name: 'Sarah', age: 35 }]);
24 | // 提取 name 属性
25 | const example = source.pipe(pluck('name'));
26 | // 输出: "Joe", "Sarah"
27 | const subscribe = example.subscribe(val => console.log(val));
28 | ```
29 |
30 | ##### 示例 2: 提取嵌套的属性
31 |
32 | (
33 | [StackBlitz](https://stackblitz.com/edit/typescript-rinjzk?file=index.ts&devtoolsheight=100)
34 | | [jsBin](http://jsbin.com/joqesidugu/1/edit?js,console) |
35 | [jsFiddle](https://jsfiddle.net/btroncone/n592m597/) )
36 |
37 | ```js
38 | // RxJS v6+
39 | import { from } from 'rxjs';
40 | import { pluck } from 'rxjs/operators';
41 |
42 | const source = from([
43 | { name: 'Joe', age: 30, job: { title: 'Developer', language: 'JavaScript' } },
44 | // 当找不到 job 属性的时候会返回 undefined
45 | { name: 'Sarah', age: 35 }
46 | ]);
47 | // 提取 job 中的 title 属性
48 | const example = source.pipe(pluck('job', 'title'));
49 | // 输出: "Developer" , undefined
50 | const subscribe = example.subscribe(val => console.log(val));
51 | ```
52 |
53 | ### 其他资源
54 |
55 | - [pluck](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-pluck) :newspaper: - 官方文档
56 |
57 | ---
58 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/pluck.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/pluck.ts)
59 |
--------------------------------------------------------------------------------
/operators/transformation/reduce.md:
--------------------------------------------------------------------------------
1 | # reduce
2 |
3 | #### 函数签名: `reduce(accumulator: function, seed: any): Observable`
4 |
5 | ## 将源 observalbe 的值归并为单个值,当源 observable 完成时将这个值发出。
6 |
7 | :bulb: 类似于 [`Array.prototype.reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce?v=a)
8 |
9 | :bulb: 如果每次发送时都需要当前的累加值,请使用 [scan](scan.md)!
10 |
11 | 
12 |
13 | ### 示例
14 |
15 | ##### 示例 1: 数字流的加和
16 |
17 | (
18 | [StackBlitz](https://stackblitz.com/edit/typescript-hdsv5e?file=index.ts&devtoolsheight=100)
19 | | [jsBin](http://jsbin.com/dakuneneho/edit?js,console) |
20 | [jsFiddle](https://jsfiddle.net/f8fw7yka/) )
21 |
22 | ```js
23 | // RxJS v6+
24 | import { of } from 'rxjs';
25 | import { reduce } from 'rxjs/operators';
26 |
27 | const source = of(1, 2, 3, 4);
28 | const example = source.pipe(reduce((acc, val) => acc + val));
29 | // 输出: Sum: 10'
30 | const subscribe = example.subscribe(val => console.log('Sum:', val));
31 | ```
32 |
33 | ### 其他资源
34 |
35 | - [reduce](http://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-reduce) :newspaper: - 官方文档
36 | - [scan() vs reduce() | RxJS 教程](https://www.youtube.com/watch?v=myEeo2rZc3g) :video_camera: - Academind
37 |
38 | ---
39 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/reduce.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/reduce.ts)
40 |
--------------------------------------------------------------------------------
/operators/transformation/scan.md:
--------------------------------------------------------------------------------
1 | # scan
2 |
3 | #### 函数签名: `scan(accumulator: function, seed: any): Observable`
4 |
5 | ## 随着时间的推移进行归并。
6 |
7 | ---
8 |
9 | :bulb: 此操作符是许多基于 [Redux](http://redux.js.org) 实现的 RxJS 的核心!
10 |
11 | ---
12 |
13 | 
14 |
15 | ### 示例
16 |
17 | ##### 示例 1: 随着时间的推移计算总数
18 |
19 | (
20 | [StackBlitz](https://stackblitz.com/edit/typescript-ltcl9d?file=index.ts&devtoolsheight=100)
21 | )
22 |
23 | ```js
24 | // RxJS v6+
25 | import { of } from 'rxjs';
26 | import { scan } from 'rxjs/operators';
27 |
28 | const source = of(1, 2, 3);
29 | // 基础的 scan 示例,从0开始,随着时间的推移计算总数
30 | const example = source.pipe(scan((acc, curr) => acc + curr, 0));
31 | // 输出累加值
32 | // 输出: 1,3,6
33 | const subscribe = example.subscribe(val => console.log(val));
34 | ```
35 |
36 | ##### 示例 2: 对对象进行累加
37 |
38 | (
39 | [StackBlitz](https://stackblitz.com/edit/typescript-vu63kz?file=index.ts&devtoolsheight=100)
40 | | [jsBin](http://jsbin.com/fusunoguqu/1/edit?js,console) |
41 | [jsFiddle](https://jsfiddle.net/btroncone/36rbu38b/) )
42 |
43 | ```js
44 | // RxJS v6+
45 | import { Subject } from 'rxjs';
46 | import { scan } from 'rxjs/operators';
47 |
48 | const subject = new Subject();
49 | // scan 示例,随着时间的推移构建对象
50 | const example = subject.pipe(
51 | scan((acc, curr) => Object.assign({}, acc, curr), {})
52 | );
53 | // 输出累加值
54 | const subscribe = example.subscribe(val =>
55 | console.log('Accumulated object:', val)
56 | );
57 | // subject 发出的值会被添加成对象的属性
58 | // {name: 'Joe'}
59 | subject.next({ name: 'Joe' });
60 | // {name: 'Joe', age: 30}
61 | subject.next({ age: 30 });
62 | // {name: 'Joe', age: 30, favoriteLanguage: 'JavaScript'}
63 | subject.next({ favoriteLanguage: 'JavaScript' });
64 | ```
65 |
66 | ##### 示例 3: 随机发出累加数组中的值。
67 |
68 | (
69 | [StackBlitz](https://stackblitz.com/edit/typescript-lb8aw9?file=index.ts&devtoolsheight=100)
70 | )
71 |
72 | ```js
73 | // RxJS v6+
74 | import { interval } from 'rxjs';
75 | import { scan, map, distinctUntilChanged } from 'rxjs/operators';
76 |
77 | // 累加数组中的值,并随机发出此数组中的值
78 | const scanObs = interval(1000)
79 | .pipe(
80 | scan((a, c) => [...a, c], []),
81 | map(r => r[Math.floor(Math.random() * r.length)]),
82 | distinctUntilChanged()
83 | )
84 | .subscribe(console.log);
85 | ```
86 |
87 | ### 相关食谱
88 |
89 | - [智能计数器](../../recipes/smartcounter.md)
90 | - [进度条](../../recipes/progressbar.md)
91 |
92 | ### 其他资源
93 |
94 | - [scan](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-scan) :newspaper: - 官方文档
95 | - [使用 RxJS 中的 reduce 和 scan 聚集流](https://egghead.io/lessons/rxjs-aggregating-streams-with-reduce-and-scan-using-rxjs) :video_camera: - Ben Lesh
96 | - [使用 scan 更新数据](https://egghead.io/lessons/rxjs-updating-data-with-scan?course=step-by-step-async-javascript-with-rxjs) :video_camera: :dollar: - John Linquist
97 | - [转换操作符: scan](https://egghead.io/lessons/rxjs-transformation-operator-scan?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
98 |
99 | ---
100 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/scan.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/scan.ts)
101 |
--------------------------------------------------------------------------------
/operators/transformation/window.md:
--------------------------------------------------------------------------------
1 | # window
2 |
3 | #### 函数签名: `window(windowBoundaries: Observable): Observable`
4 |
5 | ## 时间窗口值的 observable 。
6 |
7 | ### 示例
8 |
9 | ##### 示例 1: 打开由内部 observable 指定的窗口
10 |
11 | (
12 | [StackBlitz](https://stackblitz.com/edit/typescript-avymzq?file=index.ts&devtoolsheight=100)
13 | | [jsBin](http://jsbin.com/jituvajeri/1/edit?js,console) |
14 | [jsFiddle](https://jsfiddle.net/btroncone/rmgghg6d/) )
15 |
16 | ```js
17 | // RxJS v6+
18 | import { timer, interval } from 'rxjs';
19 | import { window, scan, mergeAll } from 'rxjs/operators';
20 |
21 | // 立即发出值,然后每秒发出值
22 | const source = timer(0, 1000);
23 | const example = source.pipe(window(interval(3000)));
24 | const count = example.pipe(scan((acc, curr) => acc + 1, 0));
25 | /*
26 | "Window 1:"
27 | 0
28 | 1
29 | 2
30 | "Window 2:"
31 | 3
32 | 4
33 | 5
34 | ...
35 | */
36 | const subscribe = count.subscribe(val => console.log(`Window ${val}:`));
37 | const subscribeTwo = example
38 | .pipe(mergeAll())
39 | .subscribe(val => console.log(val));
40 | ```
41 |
42 | ### 其他资源
43 |
44 | - [window](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-window) :newspaper: - 官方文档
45 | - [使用 window 分割 RxJS observable](https://egghead.io/lessons/rxjs-split-an-rxjs-observable-with-window?course=use-higher-order-observables-in-rxjs-effectively) :video_camera: :dollar: - André Staltz
46 |
47 | ---
48 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/window.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/window.ts)
49 |
--------------------------------------------------------------------------------
/operators/transformation/windowcount.md:
--------------------------------------------------------------------------------
1 | # windowCount
2 |
3 | #### 函数签名: `windowCount(windowSize: number, startWindowEvery: number): Observable`
4 |
5 | ## 源 observable 中的值的 observable,每次发出N个值(N由参数决定)。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 每发出x个项就开启一个新窗口
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-kcxi8y?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/nezuvacexe/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/xjgbnqp5/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { interval } from 'rxjs';
21 | import { windowCount, mergeAll, tap } from 'rxjs/operators';
22 |
23 | // 每1秒发出值
24 | const source = interval(1000);
25 | const example = source.pipe(
26 | // 每发出4个值就开启新窗口
27 | windowCount(4),
28 | tap(_ => console.log('NEW WINDOW!'))
29 | );
30 |
31 | const subscribeTwo = example
32 | .pipe(
33 | // 窗口发出嵌套的 observable
34 | mergeAll()
35 | /*
36 | 输出:
37 | "NEW WINDOW!"
38 | 0
39 | 1
40 | 2
41 | 3
42 | "NEW WINDOW!"
43 | 4
44 | 5
45 | 6
46 | 7
47 | */
48 | )
49 | .subscribe(val => console.log(val));
50 | ```
51 |
52 | ### 其他资源
53 |
54 | - [windowCount](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-windowCount) :newspaper: - 官方文档
55 |
56 | ---
57 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/windowCount.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/windowCount.ts)
58 |
--------------------------------------------------------------------------------
/operators/transformation/windowtime.md:
--------------------------------------------------------------------------------
1 | # windowTime
2 |
3 | #### 函数签名: `windowTime(windowTimeSpan: number, windowCreationInterval: number, scheduler: Scheduler): Observable`
4 |
5 | ## 在每个提供的时间跨度内,收集源 obsercvable 中的值的 observable 。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 每个指定持续时间都会开启新窗口
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-hkrjsc?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/mifayacoqo/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/g04b3qeb/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { timer } from 'rxjs';
21 | import { windowTime, tap, mergeAll } from 'rxjs/operators';
22 |
23 | // 立即发出值,然后每秒发出值
24 | const source = timer(0, 1000);
25 | const example = source.pipe(
26 | // 每3秒开启一个新窗口
27 | windowTime(3000),
28 | tap(_ => console.log('NEW WINDOW!'))
29 | );
30 |
31 | const subscribeTwo = example
32 | .pipe(
33 | // 窗口发出嵌套的 observable
34 | mergeAll()
35 | /*
36 | 输出:
37 | "NEW WINDOW!"
38 | 0
39 | 1
40 | 2
41 | "NEW WINDOW!"
42 | 3
43 | 4
44 | 5
45 | */
46 | )
47 | .subscribe(val => console.log(val));
48 | ```
49 |
50 | ### 其他资源
51 |
52 | - [windowTime](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-windowTime) :newspaper: - 官方文档
53 |
54 | ---
55 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/windowTime.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/windowTime.ts)
56 |
--------------------------------------------------------------------------------
/operators/transformation/windowtoggle.md:
--------------------------------------------------------------------------------
1 | # windowToggle
2 |
3 | #### 函数签名: `windowToggle(openings: Observable, closingSelector: function(value): Observable): Observable`
4 |
5 | ## 以 openings 发出为起始,以 closingSelector 发出为结束,收集并发出源 observable 中的值的 observable 。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 根据渐增的定时器开关窗口
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-3nsyte?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/xasofupuka/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/3xmmuzy4/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { timer, interval } from 'rxjs';
21 | import { tap, windowToggle, mergeAll } from 'rxjs/operators';
22 |
23 | // 立即发出值,然后每秒发出值
24 | const source = timer(0, 1000);
25 | // 每5秒开关窗口
26 | const toggle = interval(5000);
27 | const example = source.pipe(
28 | // 每5秒开启窗口
29 | windowToggle(toggle, val => interval(val * 1000)),
30 | tap(_ => console.log('NEW WINDOW!'))
31 | );
32 |
33 | const subscribeTwo = example
34 | .pipe(
35 | // 窗口发出嵌套的 observable
36 | mergeAll()
37 | /*
38 | 输出:
39 | "NEW WINDOW!"
40 | 5
41 | "NEW WINDOW!"
42 | 10
43 | 11
44 | "NEW WINDOW!"
45 | 15
46 | 16
47 | "NEW WINDOW!"
48 | 20
49 | 21
50 | 22
51 | */
52 | )
53 | .subscribe(val => console.log(val));
54 | ```
55 |
56 | ### 其他资源
57 |
58 | - [windowToggle](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-windowToggle) :newspaper: - 官方文档
59 | - [使用 windowToggle 有条件地分割 RxJS observable](https://egghead.io/lessons/rxjs-split-an-rxjs-observable-conditionally-with-windowtoggle?course=use-higher-order-observables-in-rxjs-effectively) :video_camera: :dollar: - André Staltz
60 |
61 | ---
62 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/windowToggle.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/windowToggle.ts)
63 |
--------------------------------------------------------------------------------
/operators/transformation/windowwhen.md:
--------------------------------------------------------------------------------
1 | # windowWhen
2 |
3 | #### 函数签名: `windowWhen(closingSelector: function(): Observable): Observable`
4 |
5 | ## 在提供的时间帧处关闭窗口,并发出从源 observable 中收集的值的 observable 。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 根据定时器开启和关闭窗口
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-52tu8k?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/tuhaposemo/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/gnx9fb3h/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { interval, timer } from 'rxjs';
21 | import { windowWhen, tap, mergeAll } from 'rxjs/operators';
22 |
23 | // 立即发出值,然后每秒发出值
24 | const source = timer(0, 1000);
25 | const example = source.pipe(
26 | // 每5秒关闭窗口并发出从源 observable 中收集的值的 observable
27 | windowWhen(() => interval(5000)),
28 | tap(_ => console.log('NEW WINDOW!'))
29 | );
30 |
31 | const subscribeTwo = example
32 | .pipe(
33 | // 窗口发出嵌套的 observable
34 | mergeAll()
35 | /*
36 | 输出:
37 | "NEW WINDOW!"
38 | 0
39 | 1
40 | 2
41 | 3
42 | 4
43 | "NEW WINDOW!"
44 | 5
45 | 6
46 | 7
47 | 8
48 | 9
49 | */
50 | )
51 | .subscribe(val => console.log(val));
52 | ```
53 |
54 | ### 其他资源
55 |
56 | - [windowWhen](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-windowWhen) :newspaper: - 官方文档
57 |
58 | ---
59 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/windowWhen.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/windowWhen.ts)
60 |
--------------------------------------------------------------------------------
/operators/utility/README.md:
--------------------------------------------------------------------------------
1 | # 工具操作符
2 |
3 | 从打印日志、处理通知到设置调度器,这些操作符提供了一些有用的工具以补充你的 observable 工具箱。
4 |
5 | ## 内容
6 |
7 | * [do / tap](do.md) :star:
8 | * [delay](delay.md) :star:
9 | * [delayWhen](delaywhen.md)
10 | * [dematerialize](dematerialize.md)
11 | * [finalize / finally](finalize.md)
12 | * [let](let.md)
13 | * [timeout](timeout.md)
14 | * [toPromise](topromise.md)
15 |
16 | :star: - _常用_
17 |
--------------------------------------------------------------------------------
/operators/utility/delay.md:
--------------------------------------------------------------------------------
1 | # delay
2 |
3 | #### 函数签名: `delay(delay: number | Date, scheduler: Scheduler): Observable`
4 |
5 | ## 根据给定时间延迟发出值。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 延迟时间持续增加
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-twjn8r?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/zebatixije/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/1kxtzcu6/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { of, merge } from 'rxjs';
21 | import { mapTo, delay } from 'rxjs/operators';
22 |
23 | // 发出一项
24 | const example = of(null);
25 | // 每延迟一次输出便增加1秒延迟时间
26 | const message = merge(
27 | example.pipe(mapTo('Hello')),
28 | example.pipe(
29 | mapTo('World!'),
30 | delay(1000)
31 | ),
32 | example.pipe(
33 | mapTo('Goodbye'),
34 | delay(2000)
35 | ),
36 | example.pipe(
37 | mapTo('World!'),
38 | delay(3000)
39 | )
40 | );
41 | // 输出: 'Hello'...'World!'...'Goodbye'...'World!'
42 | const subscribe = message.subscribe(val => console.log(val));
43 | ```
44 |
45 | ### 相关食谱
46 |
47 | - [进度条](../../recipes/progressbar.md)
48 |
49 | ### 其他资源
50 |
51 | - [delay](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-delay) :newspaper: - 官方文档
52 | - [转换操作符: delay 和 delayWhen](https://egghead.io/lessons/rxjs-transformation-operators-delay-and-delaywhen?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
53 |
54 | ---
55 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/delay.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/delay.ts)
56 |
--------------------------------------------------------------------------------
/operators/utility/delaywhen.md:
--------------------------------------------------------------------------------
1 | # delayWhen
2 |
3 | #### 函数签名: `delayWhen(selector: Function, sequence: Observable): Observable`
4 |
5 | ## 延迟发出值,延迟时间由提供函数决定。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 基于 observable 的延迟
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-5yzn8g?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/topohekuje/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/b057mxkL/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { interval, timer } from 'rxjs';
21 | import { delayWhen } from 'rxjs/operators';
22 |
23 | // 每1秒发出值
24 | const message = interval(1000);
25 | // 5秒后发出值
26 | const delayForFiveSeconds = () => timer(5000);
27 | // 5秒后,开始发出 interval 延迟的值
28 | const delayWhenExample = message.pipe(delayWhen(delayForFiveSeconds));
29 | // 延迟5秒后输出值
30 | // 例如, 输出: 5s....1...2...3
31 | const subscribe = delayWhenExample.subscribe(val => console.log(val));
32 | ```
33 |
34 | ### 其他资源
35 |
36 | - [delayWhen](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-delayWhen) :newspaper: - 官方文档
37 | - [转换操作符: delay 和 delayWhen](https://egghead.io/lessons/rxjs-transformation-operators-delay-and-delaywhen?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
38 |
39 | ---
40 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/delayWhen.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/delayWhen.ts)
41 |
--------------------------------------------------------------------------------
/operators/utility/dematerialize.md:
--------------------------------------------------------------------------------
1 | # dematerialize
2 |
3 | #### 函数签名: `dematerialize(): Observable`
4 |
5 | ## 将 notification 对象转换成 notification 值。
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 将 notifications 转换成值。
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-bxdwbg?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/vafedocibi/1/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/jw08mouy/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { from, Notification } from 'rxjs';
21 | import { dematerialize } from 'rxjs/operators';
22 |
23 | // 发出 next 和 error 通知
24 | const source = from([
25 | Notification.createNext('SUCCESS!'),
26 | Notification.createError('ERROR!')
27 | ]).pipe(
28 | // 将 notification 对象转换成 notification 值
29 | dematerialize()
30 | )
31 |
32 | // 输出: 'NEXT VALUE: SUCCESS' 'ERROR VALUE: 'ERROR!'
33 | const subscription = source.subscribe({
34 | next: val => console.log(`NEXT VALUE: ${val}`),
35 | error: val => console.log(`ERROR VALUE: ${val}`)
36 | });
37 | ```
38 |
39 | ### 其他资源
40 |
41 | - [dematerialize](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-dematerialize) :newspaper: - 官方文档
42 |
43 | ---
44 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/demterialize.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/dematerialize.ts)
45 |
--------------------------------------------------------------------------------
/operators/utility/do.md:
--------------------------------------------------------------------------------
1 | # do / tap
2 |
3 | #### 函数签名: `do(nextOrObserver: function, error: function, complete: function): Observable`
4 |
5 | ## Transparently perform actions or side-effects, such as logging.
6 | ## 透明地执行操作或副作用,比如打印日志。
7 |
8 | ---
9 |
10 | :bulb: If you are using as a pipeable operator, `do` is known as `tap`!
11 |
12 | ---
13 |
14 | 
15 |
16 | ### 示例
17 |
18 | ##### 示例 1: 使用 do 输出日志
19 |
20 | (
21 | [StackBlitz](https://stackblitz.com/edit/typescript-cd2gjp?file=index.ts&devtoolsheight=100)
22 | | [jsBin](http://jsbin.com/jimazuriva/1/edit?js,console) |
23 | [jsFiddle](https://jsfiddle.net/btroncone/qtyakorq/) )
24 |
25 | ```js
26 | // RxJS v6+
27 | import { of } from 'rxjs';
28 | import { tap, map } from 'rxjs/operators';
29 |
30 | const source = of(1, 2, 3, 4, 5);
31 | // 使用 tap 透明地打印 source 中的值
32 | const example = source.pipe(
33 | tap(val => console.log(`BEFORE MAP: ${val}`)),
34 | map(val => val + 10),
35 | tap(val => console.log(`AFTER MAP: ${val}`))
36 | );
37 |
38 | // 'tap' 并不转换值
39 | // 输出: 11...12...13...14...15
40 | const subscribe = example.subscribe(val => console.log(val));
41 | ```
42 |
43 | ### 其他资源
44 |
45 | - [do](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-do) :newspaper: - 官方文档
46 | - [使用 do 打印流](https://egghead.io/lessons/rxjs-logging-a-stream-with-do?course=step-by-step-async-javascript-with-rxjs) :video_camera: :dollar: - John Linquist
47 | - [工具操作符: do](https://egghead.io/lessons/rxjs-utility-operator-do?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz
48 |
49 | ---
50 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/do.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/do.ts)
51 |
--------------------------------------------------------------------------------
/operators/utility/finalize.md:
--------------------------------------------------------------------------------
1 | # finalize / finally
2 |
3 | #### 函数签名: `finalize(callback: () => void)`
4 |
5 | ## 当 Observable 完成或报错时调用函数
6 |
7 | ### [ 示例尽请期待! ]
8 |
9 | ### 相关食谱
10 |
11 | * [HTTP 轮询](../../recipes/http-polling.md)
12 |
13 | ### 其他资源
14 |
15 | * [finalize](http://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-finalize)
16 | :newspaper: - 官方文档
17 |
18 | ---
19 |
20 | > :file_folder: 源码:
21 | > [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/finalize.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/finalize.ts)
22 |
--------------------------------------------------------------------------------
/operators/utility/let.md:
--------------------------------------------------------------------------------
1 | # let
2 |
3 | #### 函数签名: `let(function): Observable`
4 |
5 | ## 让我拥有完整的 observable 。
6 |
7 | ---
8 |
9 | :warning: `let` is no longer available, or necessary, with pipeable operators!
10 | (RxJS 5.5+)
11 |
12 | ---
13 |
14 | 
15 |
16 | ### 示例
17 |
18 | ##### 示例 1: 使用 let 重用错误处理逻辑
19 |
20 | ( [jsBin](http://jsbin.com/rosuborara/1/edit?js,console) |
21 | [jsFiddle](https://jsfiddle.net/btroncone/qtq1h8vw/) )
22 |
23 | ```js
24 | // 自定义错误处理逻辑
25 | const retryThreeTimes = obs =>
26 | obs.retry(3).catch(_ => Rx.Observable.of('ERROR!'));
27 | const examplePromise = val =>
28 | new Promise(resolve => resolve(`Complete: ${val}`));
29 |
30 | // 伪造的请求
31 | const subscribe = Rx.Observable.of('some_url')
32 | .mergeMap(url => examplePromise(url))
33 | // 能够在使用 let 的多个地方重用错误处理逻辑
34 | .let(retryThreeTimes)
35 | // 输出: Complete: some_url
36 | .subscribe(result => console.log(result));
37 |
38 | const customizableRetry = retryTimes => obs =>
39 | obs.retry(retryTimes).catch(_ => Rx.Observable.of('ERROR!'));
40 |
41 | // 伪造的请求
42 | const secondSubscribe = Rx.Observable.of('some_url')
43 | .mergeMap(url => examplePromise(url))
44 | // 能够在使用 let 的多个地方重用错误处理逻辑
45 | .let(customizableRetry(3))
46 | // 输出: Complete: some_url
47 | .subscribe(result => console.log(result));
48 | ```
49 |
50 | ##### 示例 2: 使用 let 应用于 map
51 |
52 | ( [jsBin](http://jsbin.com/jiyupaxomo/edit?js,console) |
53 | [jsFiddle](https://jsfiddle.net/btroncone/6n7w3b22/) )
54 |
55 | ```js
56 | // 将数组作为序列发出
57 | const source = Rx.Observable.from([1, 2, 3, 4, 5]);
58 | // 演示 let 和其他操作符间的区别
59 | const test = source
60 | .map(val => val + 1)
61 | /*
62 | 取消下面一行的注释会导致失败,let 的行为不同于大多数操作符
63 | val 在这里是 observable
64 | */
65 | //.let(val => val + 2)
66 | .subscribe(val => console.log('VALUE FROM ARRAY: ', val));
67 |
68 | const subscribe = source
69 | .map(val => val + 1)
70 | // 'let' 会让你拥有整个的 observable
71 | .let(obs => obs.map(val => val + 2))
72 | // 输出: 4,5,6,7,8
73 | .subscribe(val => console.log('VALUE FROM ARRAY WITH let: ', val));
74 | ```
75 |
76 | ##### 示例 3: 使用 let 应用于多个操作符
77 |
78 | ( [jsBin](http://jsbin.com/zamizapaho/1/edit?js,console) |
79 | [jsFiddle](https://jsfiddle.net/btroncone/gxsq1woc/) )
80 |
81 | ```js
82 | // 将数组作为序列发出
83 | const source = Rx.Observable.from([1, 2, 3, 4, 5]);
84 |
85 | // let 提供了灵活性,可以将多个操作符添加到源 observale,然后返回
86 | const subscribeTwo = source
87 | .map(val => val + 1)
88 | .let(obs =>
89 | obs
90 | .map(val => val + 2)
91 | // 还可以只返回偶数
92 | .filter(val => val % 2 === 0)
93 | )
94 | // 输出: 4,6,8
95 | .subscribe(val => console.log('let WITH MULTIPLE OPERATORS: ', val));
96 | ```
97 |
98 | ##### 示例 4: 通过函数应用于操作符
99 |
100 | ( [jsBin](http://jsbin.com/vojelelamu/1/edit?js,console) |
101 | [jsFiddle](https://jsfiddle.net/btroncone/ah09dL9e/) )
102 |
103 | ```js
104 | // 将数组作为序列发出
105 | const source = Rx.Observable.from([1, 2, 3, 4, 5]);
106 |
107 | // 传入你自己的函数来将操作符添加到 observable
108 | const obsArrayPlusYourOperators = yourAppliedOperators => {
109 | return source.map(val => val + 1).let(yourAppliedOperators);
110 | };
111 | const addTenThenTwenty = obs => obs.map(val => val + 10).map(val => val + 20);
112 | const subscribe = obsArrayPlusYourOperators(addTenThenTwenty)
113 | // 输出: 32, 33, 34, 35, 36
114 | .subscribe(val => console.log('let FROM FUNCTION:', val));
115 | ```
116 |
117 | ### 其他资源
118 |
119 | - [let](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/let.md) :newspaper: - 官方文档
120 |
121 | ---
122 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/let.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/let.ts)
123 |
--------------------------------------------------------------------------------
/operators/utility/timeout.md:
--------------------------------------------------------------------------------
1 | # timeout
2 |
3 | #### 函数签名: `timeout(due: number, scheduler: Scheduler): Observable`
4 |
5 | ## 在指定时间间隔内不发出值就报错
6 |
7 | 
8 |
9 | ### 示例
10 |
11 | ##### 示例 1: 2.5秒后超时
12 |
13 | (
14 | [StackBlitz](https://stackblitz.com/edit/typescript-eegqyz?file=index.ts&devtoolsheight=100)
15 | | [jsBin](http://jsbin.com/gonakiniho/edit?js,console) |
16 | [jsFiddle](https://jsfiddle.net/btroncone/nr4e1ofy/1/) )
17 |
18 | ```js
19 | // RxJS v6+
20 | import { of } from 'rxjs';
21 | import { concatMap, timeout, catchError, delay } from 'rxjs/operators';
22 |
23 | // 模拟请求
24 | function makeRequest(timeToDelay) {
25 | return of('Request Complete!').pipe(delay(timeToDelay));
26 | }
27 |
28 | of(4000, 3000, 2000)
29 | .pipe(
30 | concatMap(duration =>
31 | makeRequest(duration).pipe(
32 | timeout(2500),
33 | catchError(error => of(`Request timed out after: ${duration}`))
34 | )
35 | )
36 | )
37 | /*
38 | * "Request timed out after: 4000"
39 | * "Request timed out after: 3000"
40 | * "Request Complete!"
41 | */
42 | .subscribe(val => console.log(val));
43 | ```
44 |
45 | ### 其他资源
46 |
47 | - [timeout](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/timeout.md)
48 | :newspaper: - 官方文档
49 |
50 | ---
51 |
52 | > :file_folder: 源码:
53 | > [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/timeout.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/timeout.ts)
54 |
--------------------------------------------------------------------------------
/operators/utility/topromise.md:
--------------------------------------------------------------------------------
1 | # toPromise
2 |
3 | #### 函数签名: `toPromise() : Promise`
4 |
5 | ## 将 observable 转换成 promise 。
6 |
7 | ---
8 |
9 | :warning: `toPromise` has been deprecated! (RxJS 5.5+)
10 |
11 | ---
12 |
13 |
14 |
15 | ### 示例
16 |
17 | ##### 示例 1: 基础的 Promise
18 |
19 | ( [jsBin](http://jsbin.com/favoqecixi/1/edit?js,console) |
20 | [jsFiddle](https://jsfiddle.net/btroncone/thykc9up/) )
21 |
22 | ```js
23 | // 返回基础的 observable
24 | const sample = val => Rx.Observable.of(val).delay(5000);
25 | // 将基础的 observable 转换成 promise
26 | const example = sample('First Example')
27 | .toPromise()
28 | // 输出: 'First Example'
29 | .then(result => {
30 | console.log('From Promise:', result);
31 | });
32 | ```
33 |
34 | ##### 示例 2: 使用 Promise.all
35 |
36 | ( [jsBin](http://jsbin.com/hutiyicaco/1/edit?js,console) |
37 | [jsFiddle](https://jsfiddle.net/btroncone/xzu6u7hs/) )
38 |
39 | ```js
40 | // 返回基础的 observable
41 | const sample = val => Rx.Observable.of(val).delay(5000);
42 | /*
43 | 将每个 observable 转换成 promise 并使用 Promise.all
44 | 来等待所有 promise 解析完成
45 | (你或许应该使用 forkJoin,而不是 toPromise)
46 | */
47 | const example = () => {
48 | return Promise.all([
49 | sample('Promise 1').toPromise(),
50 | sample('Promise 2').toPromise()
51 | ]);
52 | }
53 | // 输出: ["Promise 1", "Promise 2"]
54 | example().then(val => {
55 | console.log('Promise.all Result:', val);
56 | });
57 | ```
58 |
59 | ### 其他资源
60 |
61 | - [toPromise](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-toPromise) :newspaper: - 官方文档
62 |
63 | ---
64 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/toPromise.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/toPromise.ts)
65 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "learn-rxjs",
3 | "version": "0.0.1",
4 | "description": "Learn RxJS 5 Gitbook",
5 | "main": "",
6 | "dependencies": {},
7 | "devDependencies": {
8 | "jasmine": "^2.4.1",
9 | "rxjs": "^5.0.0-beta.2"
10 | },
11 | "scripts": {
12 | "gb": "gitbook build"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/btroncone/learn-rxjs.git"
17 | },
18 | "keywords": ["rxjs"],
19 | "author": "Brian Troncone ",
20 | "contributors": [
21 | {
22 | "name": "Huy Le"
23 | }
24 | ],
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/btroncone/learn-rxjs/issues"
28 | },
29 | "homepage": "https://github.com/btroncone/learn-rxjs#readme"
30 | }
31 |
--------------------------------------------------------------------------------
/publish.sh:
--------------------------------------------------------------------------------
1 | rm -rf ./_book
2 | mkdir about
3 | cp README.md ./about/README.md
4 | gitbook build
5 | rm -rf ./about
6 | mkdir ./_book/ebook
7 | gitbook pdf ./ ./_book/ebook/学习RxJS操作符.pdf
8 | gitbook mobi ./ ./_book/ebook/学习RxJS操作符.mobi
9 | gitbook epub ./ ./_book/ebook/学习RxJS操作符.epub
10 | git checkout gh-pages
11 | git pull origin gh-pages
12 | cp -rf ./_book/* ./
13 | git add .
14 | git commit -m 'chore(docs): regenerated book'
15 | git push origin gh-pages
--------------------------------------------------------------------------------
/recipes/README.md:
--------------------------------------------------------------------------------
1 | # 食谱
2 |
3 | 有助于学习 RxJS 的常见用例和有趣的食谱。
4 |
5 | ### 内容
6 |
7 | * [进度条](progressbar.md)
8 | * [智能计数器](smartcounter.md)
9 | * [游戏循环](gameloop.md)
10 | * [HTTP 轮询](http-polling.md)
11 |
--------------------------------------------------------------------------------
/recipes/progressbar.md:
--------------------------------------------------------------------------------
1 | # 进度条
2 |
3 | _作者 [@barryrowe](https://twitter.com/barryrowe)_
4 |
5 | 本食谱演示了动态进度条的创建、模拟多个请求的管理,并在每个请求完成后更新总体进度。
6 |
7 |
8 |
9 | ### 示例代码
10 |
11 | (
12 | [StackBlitz](https://stackblitz.com/edit/rxjs-5-progress-bar-wxdxwe?file=index.ts&devtoolsheight=50)
13 | )
14 |
15 | ```js
16 | import './style.css';
17 |
18 | import { Observable, of, empty, fromEvent, from } from 'rxjs';
19 | import {
20 | delay,
21 | switchMapTo,
22 | concatAll,
23 | count,
24 | scan,
25 | withLatestFrom,
26 | share
27 | } from 'rxjs/operators';
28 |
29 | const requestOne = of('first').pipe(delay(500));
30 | const requestTwo = of('second').pipe(delay(800));
31 | const requestThree = of('third').pipe(delay(1100));
32 | const requestFour = of('fourth').pipe(delay(1400));
33 | const requestFive = of('fifth').pipe(delay(1700));
34 |
35 | const loadButton = document.getElementById('load');
36 | const progressBar = document.getElementById('progress');
37 | const content = document.getElementById('data');
38 |
39 | // 请求完成后更新进度条
40 | const updateProgress = progressRatio => {
41 | console.log('Progress Ratio: ', progressRatio);
42 | progressBar.style.width = 100 * progressRatio + '%';
43 | if (progressRatio === 1) {
44 | progressBar.className += ' finished';
45 | } else {
46 | progressBar.className = progressBar.className.replace(' finished', '');
47 | }
48 | };
49 | // 通知更新的简单辅助函数
50 | const updateContent = newContent => {
51 | content.innerHTML += newContent;
52 | };
53 |
54 | const displayData = data => {
55 | updateContent(`${data}
`);
56 | };
57 |
58 | // 模拟5个不同时长的请求
59 | const observables: Array> = [
60 | requestOne,
61 | requestTwo,
62 | requestThree,
63 | requestFour,
64 | requestFive
65 | ];
66 |
67 | const array$ = from(observables);
68 | const requests$ = array$.pipe(concatAll());
69 | const clicks$ = fromEvent(loadButton, 'click');
70 |
71 | const progress$ = clicks$.pipe(
72 | switchMapTo(requests$),
73 | share()
74 | );
75 |
76 | const count$ = array$.pipe(count());
77 |
78 | const ratio$ = progress$.pipe(
79 | scan(current => current + 1, 0),
80 | withLatestFrom(count$, (current, count) => current / count)
81 | );
82 |
83 | clicks$.pipe(switchMapTo(ratio$)).subscribe(updateProgress);
84 |
85 | progress$.subscribe(displayData);
86 | ```
87 |
88 | ##### html
89 |
90 | ```html
91 |
94 |
95 |
98 |
99 |
100 |
101 |
102 | ```
103 |
104 | _感谢 [@johnlinquist](https://twitter.com/johnlindquist) 对本示例的帮助!_
105 |
106 | ### 使用到的操作符
107 |
108 | - [concatAll](../operators/transformation/concatall.md)
109 | - [delay](../operators/utility/delay.md)
110 | - [fromEvent](../operators/creation/fromevent.md)
111 | - [from](../operators/creation/from.md)
112 | - [scan](../operators/transformation/scan.md)
113 | - [share](../operators/multicasting/share.md)
114 | - [switchMap](../operators/transformation/switchmap.md)
115 | - [withLatestFrom](../operators/transformation/withlatestfrom.md)
116 |
--------------------------------------------------------------------------------
/styles/website.css:
--------------------------------------------------------------------------------
1 | .ua-ad {
2 | text-align: center;
3 | padding: 10px 0;
4 | }
5 |
--------------------------------------------------------------------------------