├── .gitignore ├── LICENSE ├── README.md ├── SUMMARY.md ├── asset └── Rx_Logo_M.png ├── book.json ├── concepts ├── README.md ├── operator-imports.md └── rxjs5-6.md ├── index.md ├── operators ├── README.md ├── combination │ ├── README.md │ ├── combineall.md │ ├── combinelatest.md │ ├── concat.md │ ├── concatall.md │ ├── forkjoin.md │ ├── merge.md │ ├── mergeall.md │ ├── pairwise.md │ ├── race.md │ ├── startwith.md │ ├── withlatestfrom.md │ └── zip.md ├── complete.md ├── conditional │ ├── README.md │ ├── defaultifempty.md │ └── every.md ├── creation │ ├── README.md │ ├── create.md │ ├── empty.md │ ├── from.md │ ├── fromevent.md │ ├── frompromise.md │ ├── interval.md │ ├── of.md │ ├── range.md │ ├── throw.md │ └── timer.md ├── error_handling │ ├── README.md │ ├── catch.md │ ├── retry.md │ └── retrywhen.md ├── filtering │ ├── README.md │ ├── audit.md │ ├── audittime.md │ ├── debounce.md │ ├── debouncetime.md │ ├── distinctuntilchanged.md │ ├── filter.md │ ├── first.md │ ├── ignoreelements.md │ ├── last.md │ ├── sample.md │ ├── single.md │ ├── skip.md │ ├── skipuntil.md │ ├── skipwhile.md │ ├── take.md │ ├── takeuntil.md │ ├── takewhile.md │ ├── throttle.md │ └── throttletime.md ├── multicasting │ ├── README.md │ ├── multicast.md │ ├── publish.md │ ├── share.md │ └── sharereplay.md ├── specs │ ├── combination │ │ ├── combineall-spec.ts │ │ ├── combinelatest-spec.ts │ │ ├── concat-spec.ts │ │ ├── concatall-spec.ts │ │ ├── mergeall-spec.ts │ │ └── startwith-spec.ts │ ├── error_handling │ │ └── catch-spec.ts │ ├── filtering │ │ └── first-spec.ts │ └── helpers │ │ ├── marble-testing.ts │ │ └── test-helper.ts ├── transformation │ ├── README.md │ ├── buffer.md │ ├── buffercount.md │ ├── buffertime.md │ ├── buffertoggle.md │ ├── bufferwhen.md │ ├── concatmap.md │ ├── concatmapto.md │ ├── exhaustmap.md │ ├── expand.md │ ├── groupby.md │ ├── map.md │ ├── mapto.md │ ├── mergemap.md │ ├── partition.md │ ├── pluck.md │ ├── reduce.md │ ├── scan.md │ ├── switchmap.md │ ├── window.md │ ├── windowcount.md │ ├── windowtime.md │ ├── windowtoggle.md │ └── windowwhen.md └── utility │ ├── README.md │ ├── delay.md │ ├── delaywhen.md │ ├── dematerialize.md │ ├── do.md │ ├── finalize.md │ ├── let.md │ ├── timeout.md │ └── topromise.md ├── package.json ├── publish.sh ├── recipes ├── README.md ├── gameloop.md ├── http-polling.md ├── progressbar.md └── smartcounter.md └── styles └── website.css /.gitignore: -------------------------------------------------------------------------------- 1 | _book 2 | node_modules 3 | .idea 4 | npm-debug.log 5 | CNAME -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 learn-rxjs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | logo 2 | 3 | # 学习 RxJS 操作符 4 | 5 | 本仓库是 [Learn RxJS](https://github.com/btroncone/learn-rxjs) 的中文版。 6 | 7 | 意在通过每个操作符的清晰示例及解释来进行 RxJS 的学习。 8 | 9 | > 其实原作者还有其他想法(比如, 更底层的一些概念及更丰富的实战示例),但目前来看,主要还是进行操作符的讲解,所以我将此库命名为 “学习 RxJS 操作符” 10 | 11 | 希望各位喜欢,另外配合[官方中文文档操作符篇](https://cn.rx.js.org/class/es6/Observable.js~Observable.html)来学习效果更佳。 12 | 13 | > 电子书: [pdf格式](https://github.com/RxJS-CN/learn-rxjs-operators/raw/gh-pages/ebook/学习RxJS操作符.pdf) | [mobi格式](https://github.com/RxJS-CN/learn-rxjs-operators/raw/gh-pages/ebook/学习RxJS操作符.mobi) | [epub格式](https://github.com/RxJS-CN/learn-rxjs-operators/raw/gh-pages/ebook/学习RxJS操作符.epub) 14 | -------------------------------------------------------------------------------- /asset/Rx_Logo_M.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RxJS-CN/learn-rxjs-operators/62e3b1714d6667bc652367c2ec53a23121a9e647/asset/Rx_Logo_M.png -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": "3.1.1", 3 | "title": "学习 RxJS 操作符", 4 | "structure": { 5 | "readme": "index.md" 6 | }, 7 | "plugins": [ 8 | "include-codeblock", 9 | "advanced-emoji", 10 | "prism", 11 | "highlight", 12 | "copy-code-button", 13 | "edit-link", 14 | "github", 15 | "github-buttons" 16 | ], 17 | "pluginsConfig": { 18 | "edit-link": { 19 | "base": "https://github.com/RxJS-CN/learn-rxjs-operators/tree/master", 20 | "label": "帮忙纠错" 21 | }, 22 | "github": { 23 | "url": "https://github.com/RxJS-CN/learn-rxjs-operators" 24 | }, 25 | "github-buttons": { 26 | "buttons": [{ 27 | "user" : "RxJS-CN", 28 | "repo" : "learn-rxjs-operators", 29 | "type" : "star", 30 | "size" : "small", 31 | "count": true 32 | }, { 33 | "user" : "RxJS-CN", 34 | "repo" : "learn-rxjs-operators", 35 | "type" : "fork", 36 | "size" : "small", 37 | "count": true 38 | }] 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /concepts/README.md: -------------------------------------------------------------------------------- 1 | # 概念 2 | 3 | 对于常见的 RxJS 场景和用例的简要说明。 4 | 5 | ### 内容 6 | 7 | * [RxJS v5 -> v6 升级](rxjs5-6.md) 8 | * [理解操作符导入](operator-imports.md) 9 | -------------------------------------------------------------------------------- /concepts/operator-imports.md: -------------------------------------------------------------------------------- 1 | # 理解操作符导入 2 | 3 | 在消费或创建依赖于 RxJS 的公共库时,你可能遇到处理运算符导入的问题。在项目中引入操作符最主要的方式像下面这样导入: 4 | 5 | ```js 6 | import 'rxjs/add/operator/take'; 7 | ``` 8 | 9 | 这会将导入的操作符添加到 `Observable` 的原型上,以便在整个项目中使用: 10 | 11 | [(源码)](https://github.com/ReactiveX/rxjs/blob/master/src/add/operator/take.ts) 12 | 13 | ```js 14 | import { Observable } from '../../Observable'; 15 | import { take } from '../../operator/take'; 16 | 17 | Observable.prototype.take = take; 18 | 19 | declare module '../../Observable' { 20 | interface Observable { 21 | take: typeof take; 22 | } 23 | } 24 | ``` 25 | 26 | 对于私有项目和模块,这种方法通常是“没问题的”,但当整个团队使用同一个 [npm](https://www.npmjs.com/) 包或库时再使用这种导入方式,问题就会出现。 27 | 28 |
29 | 30 | ### 简单示例 31 | 32 | 来看看问题出在哪里,假设**小A**创建了一个公有的 Angular 组件库。在这个库中需要一些操作符,下面以传统的方式进行了导入: 33 | 34 | *some-public-library.ts* 35 | 36 | ```js 37 | import 'rxjs/add/operator/take'; 38 | import 'rxjs/add/operator/concatMap'; 39 | import 'rxjs/add/operator/switchMap'; 40 | ``` 41 | 42 | **小B**引用了小A的库。尽管他并没有导入这些操作符,但他依然可以直接使用。**这可能不是什么大问题,但确实会带来一些困扰**。小B继续使用库和这些操作符,一切都很正常... 43 | 44 | 一个月后,**小A**决定更新自己的库。他不再需要 `switchMap` 或 `concatMap`,所以他删除了导入: 45 | 46 | *some-public-library.ts* 47 | 48 | ```js 49 | import 'rxjs/add/operator/take'; 50 | ``` 51 | 52 | **小B**更新了依赖并构建自己的项目,但这次失败了。他本身并没有引入 `switchMap` 或 `concatMap`,只是基于第三方的依赖才能**正常运行**。如果你并没有意识到这样会产生问题,那可能需要一点时间来弄清楚。 53 | 54 | ### 解决方案 55 | 56 | 不再使用这种导入方式: 57 | 58 | ```js 59 | import 'rxjs/add/operator/take'; 60 | ``` 61 | 62 | 我们可以这样来进行导入: 63 | 64 | ```js 65 | import { take } from 'rxjs/operator/take'; 66 | ``` 67 | 68 | 这样可以保持 `Observable` 原型的干净,这样来直接调用它们: 69 | 70 | ```js 71 | import { take } from 'rxjs/operator/take'; 72 | import { of } from 'rxjs/observable/of'; 73 | 74 | take.call( 75 | of(1,2,3), 76 | 2 77 | ); 78 | ``` 79 | 80 | 然而这样代码很快就会变得**丑陋不堪**,想象一下这样更长的调用链: 81 | 82 | ```js 83 | import { take } from 'rxjs/operator/take'; 84 | import { map } from 'rxjs/operator/map'; 85 | import { of } from 'rxjs/observable/of'; 86 | 87 | map.call( 88 | take.call( 89 | of(1,2,3), 90 | 2 91 | ), 92 | val => val + 2 93 | ); 94 | ``` 95 | 96 | 很快我们的代码块将变得几乎无法理解。我们怎样才能两全其美呢? 97 | 98 | ### Pipeable 操作符 99 | 100 | 现在 RxJS 提供了 [`pipe`](https://github.com/ReactiveX/rxjs/blob/755df9bf908108974e38aaff79887279f2cde008/src/Observable.ts#L305-L329) 辅助函数,它存在于 `Observable` 上,它缓解了操作符不在原型上所带来的问题。我们还继续使用上面丑陋的代码块: 101 | 102 | ```js 103 | import { take, map } from 'rxjs/operators'; 104 | import { of } from 'rxjs/observable/of'; 105 | 106 | map.call( 107 | take.call( 108 | of(1,2,3), 109 | 2 110 | ), 111 | val => val + 2 112 | ); 113 | ``` 114 | 115 | 并将其转换成: 116 | 117 | ```js 118 | import { take, map } from 'rxjs/operators'; 119 | import { of } from 'rxjs/observable/of'; 120 | 121 | of(1,2,3) 122 | .pipe( 123 | take(2), 124 | map(val => val + 2) 125 | ); 126 | ``` 127 | 128 | Much easier to read, right? This also has the benefit of greatly reducing the RxJS bundle size in your application. For more on this, check out [Ashwin Sureshkumar's](https://twitter.com/Sureshkumar_Ash) excellent article [Reduce Angular app bundle size using lettable operators](https://hackernoon.com/rxjs-reduce-bundle-size-using-lettable-operators-418307295e85). 129 | 130 | 代码可读性更高了,是吧?它还有额外的好处,就是可以大大减少应用中 RxJS 的打包尺寸。想深入了解,请参见 [Ashwin Sureshkumar's](https://twitter.com/Sureshkumar_Ash) 的精彩文章 [使用 lettable 操作符来减少 Angular 应用的打包尺寸](https://hackernoon.com/rxjs-reduce-bundle-size-using-lettable-operators-418307295e85)。 131 | -------------------------------------------------------------------------------- /concepts/rxjs5-6.md: -------------------------------------------------------------------------------- 1 | # RxJS v5 -> v6 升级 2 | 3 | 准备将 RxJS 从 v5 升级到 v6 ?下面是一些有用的资源: 4 | 5 | ### [RxJS-TsLint](https://github.com/ReactiveX/rxjs-tslint) 6 | 7 | TsLint 的规则已经升级至 RxJS 6 。它可以自动更新项目中的导入路径,同时还回转换成 pipeable 操作符。 8 | 9 | ### [RxJS v5.x -> v6 升级指南](https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/migration.md) 10 | 11 | 将项目中的 RxJS v5 升级至 v6 的全面指南 12 | 13 | ### [RxJS v5 和 v6 代码的交互式比对](http://reactive.how/rxjs/explorer) 14 | 15 | 演示 v5 和 v6 代码的不同之处,示例中的代码还使用了实验性的 [pipeline 操作符](https://github.com/tc39/proposal-pipeline-operator)。 16 | 17 | 18 | ### [Pipeable 操作符](http://reactive.how/rxjs/pipeable-operators) 19 | 20 | pipeable 操作符的讲解和示例。 21 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | # 学习 RxJS 2 | 3 | RxJS 的清晰示例、解释及资源。 4 | 5 | _作者 [@btroncone](https://twitter.com/BTroncone)_ 6 | 7 | ## 前言 8 | 9 | [RxJS](https://github.com/ReactiveX/rxjs) 是当前 web 开发中最热门的库之一。它提供强大的功能性方法来处理事件,并将集成点集中到越来越多的框架、库和实用程序中,这一切使得学习 Rx 变得前所未有的吸引人。并且它还有能力利用你之前所掌握的语言知识,因为它[几乎涵盖了所有语言](http://reactivex.io/languages.html)。如果熟练掌握响应式编程 (reactive programming) 的话,那它所提供的一切似乎都可以变得很容易。 10 | 11 | **但是...** 12 | 13 | 学习 RxJS 和响应式编程很[难](https://twitter.com/hoss/status/742643506536153088)。它有着众多的概念,大量的表层 API 和从[命令式到声明式风格](http://codenugget.co/2015/03/05/declarative-vs-imperative-programming-web.html)的思维转换。本网站致力于让这些概念更容易理解,示例清晰且容易解释,并且功能参考了网络上最好的 RxJS 相关资源。目标是增强[官方文档](http://reactivex.io/rxjs/),并且提供了一个全新的,新鲜的视角,以清除任何学习上的障碍和解决初学者的痛点。学习 Rx 或许是困难的,但是它绝对值得你去付出! 14 | 15 |
16 | 17 | ## 内容 18 | 19 | #### 操作符 20 | 21 | 操作符是 observables 背后的马力,为复杂的异步任务提供了一种优雅的声明式解决方案。本章节涵盖了所有 [RxJS 操作符](/operators/README.md),还附带在 [JSBin](https://jsbin.com) 和 [JSFiddle](https://jsfiddle.net) 均可执行的清晰示例。适当的时候,还会为每个操作符提供其他资源和使用技巧的链接。 22 | 23 | ##### 分类 24 | 25 | - [组合](/operators/combination/README.md) 26 | - [条件](/operators/conditional/README.md) 27 | - [创建](/operators/creation/README.md) 28 | - [错误处理](/operators/error_handling/README.md) 29 | - [多播](/operators/multicasting/README.md) 30 | - [过滤](/operators/filtering/README.md) 31 | - [转换](/operators/transformation/README.md) 32 | - [工具](/operators/utility/README.md) 33 | 34 | **或者...** 35 | 36 | [按字母顺序排列的完整列表](/operators/complete.md) 37 | 38 | #### 概念 39 | 40 | 如果对 Observables 背后的工作原理没有扎实的基础知识,很容易感觉 RxJS 就像是“魔法”一般。本章节有助于巩固所需的主要概念,以便对响应式编程和 Observables 得心应手。 41 | 42 | [完整的概念列表](/concepts/README.md) 43 | 44 | #### 食谱 45 | 46 | 食谱用于使用 RxJS 的常用用例和有趣的解决方案。 47 | 48 | [完整的食谱列表](/recipes/README.md) 49 | 50 | ## 介绍性资源 51 | 52 | RxJS 和响应式编程的新手?除了本网站上发现的内容,这些优秀的文章和视频也将有助于你快速积累学习经验! 53 | 54 | #### 阅读 55 | 56 | - [RxJS 入门](http://reactivex.io/rxjs/manual/overview.html#introduction) - 官方文档 57 | - [不容错过的响应式编程-介绍](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) - André Staltz 58 | 59 | #### 视频 60 | 61 | - [异步编程: 循环终结者](https://egghead.io/courses/mastering-asynchronous-programming-the-end-of-the-loop) - Jafar Husain 62 | - [什么是 RxJS ?](https://egghead.io/lessons/rxjs-what-is-rxjs) - Ben Lesh 63 | - [从零开始创建 Observable](https://egghead.io/lessons/rxjs-creating-observable-from-scratch) - Ben Lesh 64 | - [介绍 RxJS 的弹珠测试](https://egghead.io/lessons/rxjs-introduction-to-rxjs-marble-testing) - Brian Troncone 65 | - [介绍响应式编程](https://egghead.io/courses/introduction-to-reactive-programming) :dollar: - André Staltz 66 | - [使用 Observables 的响应式编程](https://www.youtube.com/watch?v=HT7JiiqnYYc&feature=youtu.be) - Jeremy Lund 67 | 68 | #### 练习 69 | 70 | - [JavaScript 中的函数式编程](http://reactivex.io/learnrx/) - Jafar Husain 71 | 72 | #### 工具 73 | 74 | - [Rx Marbles - Rx Observables 的交互弹珠图](http://rxmarbles.com/) - André Staltz 75 | - [Rx Visualizer - Rx Observables 的动画游乐场](https://rxviz.com) - Misha Moroshko 76 | - [Reactive.how - 学习响应式编程的动画卡片](http://reactive.how) - Cédric Soulas 77 | 78 | *对 RxJS 4 感兴趣? 请查阅 [Denis Stoyanov's](https://github.com/xgrommx) 超棒的 [电子书](https://xgrommx.github.io/rx-book/)!* 79 | 80 | ## 翻译 81 | 82 | - [简体中文](https://rxjs-cn.github.io/learn-rxjs-operators) 83 | 84 | ### 参考文献注 85 | 86 | 本 GitBook 中包含的所有参考资料都是学习资源,其中有免费的,也有付费,它们在我学习 RxJS 时给予了极大的帮助。如果您遇到您认为应该包含在此处的文章或视频,请使用顶部菜单中的*编辑此页面*链接并提交 pull request (译者注:请去英文站提交相关链接的 PR,中文站中的 PR 是用来修复翻译中的问题)。对于您的反馈我将深表感谢! -------------------------------------------------------------------------------- /operators/README.md: -------------------------------------------------------------------------------- 1 | # 通过示例来学习 RxJS 操作符 2 | 3 | RxJS 操作符的完整列表,每个操作符都有着清晰的解释、相关资源和可执行的示例。 4 | 5 | _[更喜欢按字母顺序排列的完整列表?](complete.md)_ 6 | 7 | ### 内容 (按操作符类型) 8 | 9 | - [组合](combination/README.md) 10 | - [combineAll](combination/combineall.md) 11 | - [combineLatest](combination/combinelatest.md) :star: 12 | - [concat](combination/concat.md) :star: 13 | - [concatAll](combination/concatall.md) 14 | - [forkJoin](combination/forkjoin.md) 15 | - [merge](combination/merge.md) :star: 16 | - [mergeAll](combination/mergeall.md) 17 | - [race](combination/race.md) 18 | - [startWith](combination/startwith.md) :star: 19 | - [withLatestFrom](combination/withlatestfrom.md) :star: 20 | - [zip](combination/zip.md) 21 | - [条件](conditional/README.md) 22 | - [defaultIfEmpty](conditional/defaultifempty.md) 23 | - [every](conditional/every.md) 24 | - [创建](creation/README.md) 25 | - [create](creation/create.md) 26 | - [empty](creation/empty.md) 27 | - [from](creation/from.md) :star: 28 | - [fromEvent](creation/fromevent.md) 29 | - [fromPromise](creation/frompromise.md) :star: 30 | - [interval](creation/interval.md) 31 | - [of](creation/of.md) :star: 32 | - [range](creation/range.md) 33 | - [throw](creation/throw.md) 34 | - [timer](creation/timer.md) 35 | - [错误处理](error_handling/README.md) 36 | - [catch / catchError](error_handling/catch.md) :star: 37 | - [retry](error_handling/retry.md) 38 | - [retryWhen](error_handling/retrywhen.md) 39 | - [过滤](filtering/README.md) 40 | - [audit](filtering/audit.md) 41 | - [auditTime](filtering/audittime.md) 42 | - [debounce](filtering/debounce.md) 43 | - [debounceTime](filtering/debouncetime.md) :star: 44 | - [distinctUntilChanged](filtering/distinctuntilchanged.md) :star: 45 | - [filter](filtering/filter.md) :star: 46 | - [first](filtering/first.md) 47 | - [ignoreElements](filtering/ignoreelements.md) 48 | - [last](filtering/last.md) 49 | - [sample](filtering/sample.md) 50 | - [single](filtering/single.md) 51 | - [skip](filtering/skip.md) 52 | - [skipUntil](filtering/skipuntil.md) 53 | - [skipWhile](filtering/skipwhile.md) 54 | - [take](filtering/take.md) :star: 55 | - [takeUntil](filtering/takeuntil.md) :star: 56 | - [takeWhile](filtering/takewhile.md) 57 | - [throttle](filtering/throttle.md) 58 | - [throttleTime](filtering/throttletime.md) 59 | - [多播](multicasting/README.md) 60 | - [multicast](multicasting/multicast.md) 61 | - [publish](multicasting/publish.md) 62 | - [share](multicasting/share.md) :star: 63 | - [转换](transformation/README.md) 64 | - [buffer](transformation/buffer.md) 65 | - [bufferCount](transformation/buffercount.md) 66 | - [bufferTime](transformation/buffertime.md) :star: 67 | - [bufferToggle](transformation/buffertoggle.md) 68 | - [bufferWhen](transformation/bufferwhen.md) 69 | - [concatMap](transformation/concatmap.md) :star: 70 | - [concatMapTo](transformation/concatmapto.md) 71 | - [expand](transformation/expand.md) 72 | - [exhaustMap](transformation/exhaustmap.md) 73 | - [groupBy](transformation/groupby.md) 74 | - [map](transformation/map.md) :star: 75 | - [mapTo](transformation/mapto.md) 76 | - [mergeMap / flatMap](transformation/mergemap.md) :star: 77 | - [partition](transformation/partition.md) 78 | - [pluck](transformation/pluck.md) 79 | - [reduce](transformation/reduce.md) 80 | - [scan](transformation/scan.md) :star: 81 | - [switchMap](transformation/switchmap.md) :star: 82 | - [window](transformation/window.md) 83 | - [windowCount](transformation/windowcount.md) 84 | - [windowTime](transformation/windowtime.md) 85 | - [windowToggle](transformation/windowtoggle.md) 86 | - [windowWhen](transformation/windowwhen.md) 87 | - [工具](utility/README.md) 88 | - [do / tap](utility/do.md) :star: 89 | - [delay](utility/delay.md) :star: 90 | - [delayWhen](utility/delaywhen.md) 91 | - [finalize / finally](operators/utility/finalize.md) 92 | - [let](utility/let.md) 93 | - [timeout](utility/timeout.md) 94 | - [toPromise](utility/topromise.md) 95 | 96 | :star: - *常用* 97 | 98 | ### 其他资源 99 | 100 | - [什么是操作符?](https://cn.rx.js.org/manual/overview.html#29) :newspaper: - 官方文档 101 | - [操作符是什么?](https://egghead.io/lessons/rxjs-what-rxjs-operators-are) :video_camera: :dollar: - André Staltz 102 | -------------------------------------------------------------------------------- /operators/combination/README.md: -------------------------------------------------------------------------------- 1 | # 组合操作符 2 | 3 | 组合操作符允许连接来自多个 observables 的信息。 4 | 发出值的顺序、时间以及结构是这些操作符的主要变化。 5 | 6 | ## 内容 7 | 8 | * [combineAll](combineall.md) 9 | * [combineLatest](combinelatest.md) :star: 10 | * [concat](concat.md) :star: 11 | * [concatAll](concatall.md) 12 | * [forkJoin](forkjoin.md) 13 | * [merge](merge.md) :star: 14 | * [mergeAll](mergeall.md) 15 | * [pairwise](pairwise.md) 16 | * [race](race.md) 17 | * [startWith](startwith.md) :star: 18 | * [withLatestFrom](withlatestfrom.md) :star: 19 | * [zip](zip.md) 20 | 21 | :star: - *常用* 22 | -------------------------------------------------------------------------------- /operators/combination/combineall.md: -------------------------------------------------------------------------------- 1 | # combineAll 2 | 3 | #### 函数签名: `combineAll(project: function): Observable` 4 | 5 | ## 当源 observable 完成时,对收集的 observables 使用 [combineLatest](combinelatest.md) 。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ( [示例测试](https://github.com/btroncone/learn-rxjs/blob/master/operators/specs/combination/combineall-spec.ts) ) 12 | 13 | ##### 示例 1: 映射成内部的 interval observable 14 | 15 | ( 16 | [StackBlitz](https://stackblitz.com/edit/typescript-fbxfyh?file=index.ts&devtoolsheight=100) 17 | ) 18 | 19 | ```js 20 | // RxJS v6+ 21 | import { take, map, combineAll } from 'rxjs/operators'; 22 | import { interval } from 'rxjs'; 23 | 24 | // 每秒发出值,并只取前2个 25 | const source = interval(1000).pipe(take(2)); 26 | // 将 source 发出的每个值映射成取前5个值的 interval observable 27 | const example = source.pipe( 28 | map(val => interval(1000).pipe(map(i => `Result (${val}): ${i}`), take(5))) 29 | ); 30 | /* 31 | soure 中的2个值会被映射成2个(内部的) interval observables, 32 | 这2个内部 observables 每秒使用 combineLatest 策略来 combineAll, 33 | 每当任意一个内部 observable 发出值,就会发出每个内部 observable 的最新值。 34 | */ 35 | const combined = example.pipe(combineAll()); 36 | /* 37 | 输出: 38 | ["Result (0): 0", "Result (1): 0"] 39 | ["Result (0): 1", "Result (1): 0"] 40 | ["Result (0): 1", "Result (1): 1"] 41 | ["Result (0): 2", "Result (1): 1"] 42 | ["Result (0): 2", "Result (1): 2"] 43 | ["Result (0): 3", "Result (1): 2"] 44 | ["Result (0): 3", "Result (1): 3"] 45 | ["Result (0): 4", "Result (1): 3"] 46 | ["Result (0): 4", "Result (1): 4"] 47 | */ 48 | const subscribe = combined.subscribe(val => console.log(val)); 49 | ``` 50 | 51 | ### 其他资源 52 | 53 | * [combineAll](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-combineAll) :newspaper: - 官方文档 54 | 55 | --- 56 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/combineAll.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/combineAll.ts) 57 | -------------------------------------------------------------------------------- /operators/combination/concat.md: -------------------------------------------------------------------------------- 1 | # concat 2 | 3 | #### 函数签名: `concat(observables: ...*): Observable` 4 | 5 | ## 按照顺序,前一个 observable 完成了再订阅下一个 observable 并发出值。 6 | 7 | --- 8 | 9 | :bulb: 你可以把 concat 想象成 ATM 机前的长队,下一次交易 (subscription) 不能在前一个交易完成前开始! 10 | 11 | :bulb: 此操作符可以既有静态方法,又有实例方法! 12 | 13 | :bulb: 如果生产量是首要考虑的,而不需要关心产生值的顺序,那么试试用 [merge](merge.md) 来代替! 14 | 15 | --- 16 | 17 |
18 | 19 | ### 示例 20 | 21 | ( [示例测试](https://github.com/btroncone/learn-rxjs/blob/master/operators/specs/combination/concat-spec.ts) ) 22 | 23 | ##### 示例 1: concat 2个基础的 observables 24 | 25 | ( 26 | [StackBlitz](https://stackblitz.com/edit/typescript-ec6wed?file=index.ts&devtoolsheight=100) 27 | | [jsBin](http://jsbin.com/gegubutele/1/edit?js,console) | 28 | [jsFiddle](https://jsfiddle.net/btroncone/rxwnr3hh/) ) 29 | 30 | ```js 31 | // RxJS v6+ 32 | import { concat } from 'rxjs/operators'; 33 | import { of } from 'rxjs'; 34 | 35 | // 发出 1,2,3 36 | const sourceOne = of(1, 2, 3); 37 | // 发出 4,5,6 38 | const sourceTwo = of(4, 5, 6); 39 | // 先发出 sourceOne 的值,当完成时订阅 sourceTwo 40 | const example = sourceOne.pipe(concat(sourceTwo)); 41 | // 输出: 1,2,3,4,5,6 42 | const subscribe = example.subscribe(val => 43 | console.log('Example: Basic concat:', val) 44 | ); 45 | ``` 46 | 47 | ##### 示例 2: concat 作为静态方法 48 | 49 | ( 50 | [StackBlitz](https://stackblitz.com/edit/typescript-ks8chl?file=index.ts&devtoolsheight=100) 51 | | [jsBin](http://jsbin.com/xihagewune/1/edit?js,console) | 52 | [jsFiddle](https://jsfiddle.net/btroncone/5qdtvhu8/) ) 53 | 54 | ```js 55 | // RxJS v6+ 56 | import { of, concat } from 'rxjs'; 57 | 58 | // 发出 1,2,3 59 | const sourceOne = of(1, 2, 3); 60 | // 发出 4,5,6 61 | const sourceTwo = of(4, 5, 6); 62 | 63 | // 作为静态方法使用 64 | const example = concat(sourceOne, sourceTwo); 65 | // 输出: 1,2,3,4,5,6 66 | const subscribe = example.subscribe(val => console.log(val)); 67 | ``` 68 | 69 | ##### 示例 3: 使用延迟的 souce observable 进行 concat 70 | 71 | ( 72 | [StackBlitz](https://stackblitz.com/edit/typescript-vsphry?file=index.ts&devtoolsheight=100) 73 | | [jsBin](http://jsbin.com/nezonosubi/1/edit?js,console) | 74 | [jsFiddle](https://jsfiddle.net/btroncone/L2s49msx/) ) 75 | 76 | ```js 77 | // RxJS v6+ 78 | import { delay, concat } from 'rxjs/operators'; 79 | import { of } from 'rxjs'; 80 | 81 | // 发出 1,2,3 82 | const sourceOne = of(1, 2, 3); 83 | // 发出 4,5,6 84 | const sourceTwo = of(4, 5, 6); 85 | 86 | // 延迟3秒,然后发出 87 | const sourceThree = sourceOne.pipe(delay(3000)); 88 | // sourceTwo 要等待 sourceOne 完成才能订阅 89 | const example = sourceThree.pipe(concat(sourceTwo)); 90 | // 输出: 1,2,3,4,5,6 91 | const subscribe = example.subscribe(val => 92 | console.log('Example: Delayed source one:', val) 93 | ); 94 | ``` 95 | 96 | ##### 示例 4: 使用不完成的 source observable 进行 concat 97 | 98 | ( 99 | [StackBlitz](https://stackblitz.com/edit/typescript-njc2jw?file=index.ts&devtoolsheight=100) 100 | | [jsBin](http://jsbin.com/vixajoxaze/1/edit?js,console) | 101 | [jsFiddle](https://jsfiddle.net/btroncone/4bhtb81u/) ) 102 | 103 | ```js 104 | // RxJS v6+ 105 | import { interval, of, concat } from 'rxjs'; 106 | 107 | // 当 source 永远不完成时,随后的 observables 永远不会运行 108 | const source = concat(interval(1000), of('This', 'Never', 'Runs')); 109 | // 输出: 0,1,2,3,4.... 110 | const subscribe = source.subscribe(val => 111 | console.log( 112 | 'Example: Source never completes, second observable never runs:', 113 | val 114 | ) 115 | // 输出: 0,1,2,3,4.... 116 | const subscribe = source.subscribe(val => console.log('Example: Source never completes, second observable never runs:', val)); 117 | ``` 118 | 119 | 120 | ### 其他资源 121 | 122 | * [concat](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-concat) :newspaper: - 官方文档 123 | * [组合操作符: concat, startWith](https://egghead.io/lessons/rxjs-combination-operators-concat-startwith?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz 124 | 125 | --- 126 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/concat.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/concat.ts) 127 | -------------------------------------------------------------------------------- /operators/combination/concatall.md: -------------------------------------------------------------------------------- 1 | # concatAll 2 | 3 | #### 函数签名: `concatAll(): Observable` 4 | 5 | ## 收集 observables,当前一个完成时订阅下一个。 6 | 7 | --- 8 | 9 | :warning: 当源 observable 发出的速度要比内部 observables 完成更快时,请小心 [backpressure (背压)](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/backpressure.md) ! 10 | 11 | :bulb: 在很多情况下,你可以使用只使用单个操作符 [concatMap](../transformation/concatmap.md) 来替代! 12 | > 译者注:concatMap === map + concatAll 13 | 14 | --- 15 | 16 |
17 | 18 | ### 示例 19 | 20 | ( [示例测试](https://github.com/btroncone/learn-rxjs/blob/master/operators/specs/combination/concatall-spec.ts) ) 21 | 22 | ##### 示例 1: 使用 observable 来进行 concatAll 23 | 24 | ( 25 | [StackBlitz](https://stackblitz.com/edit/typescript-zwtpc7?file=index.ts&devtoolsheight=100) 26 | | [jsBin](http://jsbin.com/nakinenuva/1/edit?js,console) | 27 | [jsFiddle](https://jsfiddle.net/btroncone/8dfuf2y6/) ) 28 | 29 | ```js 30 | // RxJS v6+ 31 | import { map, concatAll } from 'rxjs/operators'; 32 | import { of, interval } from 'rxjs'; 33 | 34 | // 每2秒发出值 35 | const source = interval(2000); 36 | const example = source.pipe( 37 | // 为了演示,增加10并作为 observable 返回 38 | map(val => of(val + 10)), 39 | // 合并内部 observables 的值 40 | concatAll() 41 | ); 42 | // 输出: 'Example with Basic Observable 10', 'Example with Basic Observable 11'... 43 | const subscribe = example.subscribe(val => 44 | console.log('Example with Basic Observable:', val) 45 | ); 46 | ``` 47 | 48 | ##### 示例 2: 使用 promise 来进行 concatAll 49 | 50 | ( 51 | [StackBlitz](https://stackblitz.com/edit/typescript-3w4px3?file=index.ts&devtoolsheight=100) 52 | | [jsBin](http://jsbin.com/bekegeyopu/1/edit?js,console) | 53 | [jsFiddle](https://jsfiddle.net/btroncone/w7kp7qLs/) ) 54 | 55 | ```js 56 | // RxJS v6+ 57 | import { map, concatAll } from 'rxjs/operators'; 58 | import { interval } from 'rxjs'; 59 | 60 | // 创建并解析一个基础的 promise 61 | const samplePromise = val => new Promise(resolve => resolve(val)); 62 | // 每2秒发出值 63 | const source = interval(2000); 64 | 65 | const example = source.pipe( 66 | map(val => samplePromise(val)), 67 | // 合并解析过的 promise 的值 68 | concatAll() 69 | ); 70 | // 输出: 'Example with Promise 0', 'Example with Promise 1'... 71 | const subscribe = example.subscribe(val => 72 | console.log('Example with Promise:', val) 73 | ); 74 | ``` 75 | 76 | ##### 示例 3: 当内部 observables 完成时进行延迟 77 | 78 | ( 79 | [StackBlitz](https://stackblitz.com/edit/typescript-ft3rbf?file=index.ts&devtoolsheight=100) 80 | | [jsBin](http://jsbin.com/pojolatile/1/edit?js,console) | 81 | [jsFiddle](https://jsfiddle.net/btroncone/8230ucbg/) ) 82 | 83 | ```js 84 | // RxJS v6+ 85 | import { take, concatAll } from 'rxjs/operators'; 86 | import { interval, of } from 'rxjs/observable/interval'; 87 | 88 | const obs1 = interval(1000).pipe(take(5)); 89 | const obs2 = interval(500).pipe(take(2)); 90 | const obs3 = interval(2000).pipe(take(1)); 91 | // 发出3个 observables 92 | const source = of(obs1, obs2, obs3); 93 | // 按顺序订阅每个内部 obserable,前一个完成了再订阅下一个 94 | const example = source.pipe(concatAll()); 95 | /* 96 | 输出: 0,1,2,3,4,0,1,0 97 | 怎么运行的... 98 | 订阅每个内部 observable 并发出值,当一个完成了才订阅下一个 99 | obs1: 0,1,2,3,4 (complete) 100 | obs2: 0,1 (complete) 101 | obs3: 0 (complete) 102 | */ 103 | 104 | const subscribe = example.subscribe(val => console.log(val)); 105 | ``` 106 | 107 | ### 相关食谱 108 | 109 | - [进度条](../../recipes/progressbar.md) 110 | 111 | ### 其他资源 112 | 113 | - [concatAll](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-concatAll) :newspaper: - 官方文档 114 | - [使用 RxJS 的 concatAll 来打平高阶 observable](https://egghead.io/lessons/rxjs-flatten-a-higher-order-observable-with-concatall-in-rxjs?course=use-higher-order-observables-in-rxjs-effectively) :video_camera: :dollar: - André Staltz 115 | 116 | --- 117 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/concatAll.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/concatAll.ts) 118 | -------------------------------------------------------------------------------- /operators/combination/merge.md: -------------------------------------------------------------------------------- 1 | # merge 2 | 3 | #### 函数签名: `merge(input: Observable): Observable` 4 | 5 | ## 将多个 observables 转换成单个 observable 。 6 | 7 | --- 8 | 9 | :bulb: 此操作符可以既有静态方法,又有实例方法! 10 | 11 | :bulb: 如果产生值的顺序是首要考虑的,那么试试用 [concat](concat.md) 来代替! 12 | 13 | --- 14 | 15 |
16 | 17 | ### 示例 18 | 19 | ##### 示例 1: 使用静态方法合并多个 observables 20 | 21 | ( 22 | [StackBlitz](https://stackblitz.com/edit/typescript-ohq6rx?file=index.ts&devtoolsheight=100) 23 | | [jsBin](http://jsbin.com/conufujapi/1/edit?js,console) | 24 | [jsFiddle](https://jsfiddle.net/btroncone/qvq9dscu/) ) 25 | 26 | ```js 27 | // RxJS v6+ 28 | import { mapTo } from 'rxjs/operators'; 29 | import { interval, merge } from 'rxjs'; 30 | 31 | // 每2.5秒发出值 32 | const first = interval(2500); 33 | // 每2秒发出值 34 | const second = interval(2000); 35 | // 每1.5秒发出值 36 | const third = interval(1500); 37 | // 每1秒发出值 38 | const fourth = interval(1000); 39 | 40 | // 从一个 observable 中发出输出值 41 | const example = merge( 42 | first.pipe(mapTo('FIRST!')), 43 | second.pipe(mapTo('SECOND!')), 44 | third.pipe(mapTo('THIRD')), 45 | fourth.pipe(mapTo('FOURTH')) 46 | ); 47 | // 输出: "FOURTH", "THIRD", "SECOND!", "FOURTH", "FIRST!", "THIRD", "FOURTH" 48 | const subscribe = example.subscribe(val => console.log(val)); 49 | ``` 50 | 51 | ##### 示例 2: 使用实例方法合并2个 observables 52 | 53 | ( 54 | [StackBlitz](https://stackblitz.com/edit/typescript-bcsl1r?file=index.ts&devtoolsheight=100) 55 | | [jsBin](http://jsbin.com/wuwujokaqu/1/edit?js,console) | 56 | [jsFiddle](https://jsfiddle.net/btroncone/me5ofcr0/) ) 57 | 58 | ```js 59 | // RxJS v6+ 60 | import { merge } from 'rxjs/operators'; 61 | import { interval } from 'rxjs'; 62 | 63 | // 每2.5秒发出值 64 | const first = interval(2500); 65 | // 每1秒发出值 66 | const second = interval(1000); 67 | // 作为实例方法使用 68 | const example = first.pipe(merge(second)); 69 | // 输出: 0,1,0,2.... 70 | const subscribe = example.subscribe(val => console.log(val)); 71 | ``` 72 | 73 | ### 相关食谱 74 | 75 | - [HTTP 轮询](../../recipes/http-polling.md) 76 | 77 | ### 其他资源 78 | 79 | - [merge](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-merge) :newspaper: - 官方文档 80 | - [使用 merge 处理多个流](https://egghead.io/lessons/rxjs-handling-multiple-streams-with-merge?course=step-by-step-async-javascript-with-rxjs) :video_camera: :dollar: - John Linquist 81 | - [使用 merge1 共享网络请求](https://egghead.io/lessons/rxjs-reactive-programming-sharing-network-requests-with-rxjs-merge?course=introduction-to-reactive-programming) :video_camera: :dollar: - André Staltz 82 | - [组合操作符: merge](https://egghead.io/lessons/rxjs-combination-operator-merge?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz 83 | 84 | --- 85 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/merge.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/merge.ts) 86 | -------------------------------------------------------------------------------- /operators/combination/mergeall.md: -------------------------------------------------------------------------------- 1 | # mergeAll 2 | 3 | #### 函数签名: `mergeAll(concurrent: number): Observable` 4 | 5 | ## 收集并订阅所有的 observables 。 6 | 7 | --- 8 | 9 | :bulb: 在很多情况下,你可以使用只使用单个操作符 [mergeMap](../transformation/mergemap.md) 来替代! 10 | > 译者注:mergeMap === map + mergeAll 11 | 12 | --- 13 | 14 |
15 | 16 | ### 示例 17 | 18 | ( [示例测试](https://github.com/btroncone/learn-rxjs/blob/master/operators/specs/combination/mergeall-spec.ts) ) 19 | 20 | ##### 示例 1: 使用 promise 来进行 concatAll 21 | 22 | ( 23 | [StackBlitz](https://stackblitz.com/edit/typescript-y4ncvc?file=index.ts&devtoolsheight=100) 24 | | [jsBin](http://jsbin.com/worecuhiba/1/edit?js,console) | 25 | [jsFiddle](https://jsfiddle.net/btroncone/0sc4nsxa/) ) 26 | 27 | ```js 28 | // RxJS v6+ 29 | import { map, mergeAll } from 'rxjs/operators'; 30 | import { of } from 'rxjs'; 31 | 32 | const myPromise = val => 33 | new Promise(resolve => setTimeout(() => resolve(`Result: ${val}`), 2000)); 34 | // 发出 1,2,3 35 | const source = of(1, 2, 3); 36 | 37 | const example = source.pipe( 38 | // 将每个值映射成 promise 39 | map(val => myPromise(val)), 40 | // 发出 source 的结果 41 | mergeAll() 42 | ); 43 | 44 | /* 45 | 输出: 46 | "Result: 1" 47 | "Result: 2" 48 | "Result: 3" 49 | */ 50 | const subscribe = example.subscribe(val => console.log(val)); 51 | ``` 52 | 53 | ##### 示例 2: 使用**并发的**参数来进行 mergeAll 54 | 55 | ( 56 | [StackBlitz](https://stackblitz.com/edit/typescript-xpaqjh?file=index.ts&devtoolsheight=100) 57 | | [jsFiddle](https://jsfiddle.net/zra3zxhs/) ) 58 | 59 | ```js 60 | // RxJS v6+ 61 | import { take, map, delay, mergeAll } from 'rxjs/operators'; 62 | import { interval } from 'rxjs'; 63 | 64 | const source = interval(500).pipe(take(5)); 65 | 66 | /* 67 | interval 每0.5秒发出一个值。这个值会被映射成延迟1秒的 interval 。mergeAll 操作符接收一个可选参数 68 | 以决定在同一时间有多少个内部 observables 可以被订阅。其余的 observables 会先暂存以等待订阅。 69 | */ 70 | const example = source 71 | .pipe( 72 | map(val => 73 | source.pipe( 74 | delay(1000), 75 | take(3) 76 | ) 77 | ), 78 | mergeAll(2) 79 | ) 80 | .subscribe(val => console.log(val)); 81 | /* 82 | 一旦操作符发出了所有值,则 subscription 完成。 83 | */ 84 | ``` 85 | 86 | ### 其他资源 87 | 88 | - [mergeAll](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-mergeAll) :newspaper: - 官方文档 89 | - [[使用 RxJS 的 mergeAll 来打平高阶 observable](https://egghead.io/lessons/rxjs-flatten-a-higher-order-observable-with-mergeall-in-rxjs?course=use-higher-order-observables-in-rxjs-effectively) :video_camera: :dollar: - André Staltz 90 | 91 | --- 92 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/mergeAll.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/mergeAll.ts) 93 | -------------------------------------------------------------------------------- /operators/combination/pairwise.md: -------------------------------------------------------------------------------- 1 | # pairwise 2 | 3 | #### 函数签名: `pairwise(): Observable` 4 | 5 | ## 将前一个值和当前值作为数组发出 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-tkuydr?file=index.ts&devtoolsheight=50) 15 | | [jsBin](http://jsbin.com/keteyahido/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/8va47bq3/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { pairwise, take } from 'rxjs/operators'; 21 | import { interval } from 'rxjs'; 22 | 23 | // 返回: [0,1], [1,2], [2,3], [3,4], [4,5] 24 | interval(1000) 25 | .pipe( 26 | pairwise(), 27 | take(5) 28 | ) 29 | .subscribe(console.log); 30 | ``` 31 | 32 | ### 其他资源 33 | 34 | - [pairwise](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-pairwise) :newspaper: - 官方文档 35 | 36 | --- 37 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/pairwise.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/pairwise.ts) 38 | -------------------------------------------------------------------------------- /operators/combination/race.md: -------------------------------------------------------------------------------- 1 | # race 2 | 3 | #### 函数签名: `race(): Observable` 4 | 5 | ## 使用首先发出值的 observable 。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 使用 4个 observables 进行 race 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-cvfmug?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/goqiwobeno/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/8jcmb1ec/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { mapTo } from 'rxjs/operators'; 21 | import { interval } from 'rxjs/observable/interval'; 22 | import { race } from 'rxjs/observable/race'; 23 | 24 | // 接收第一个发出值的 observable 25 | const example = race( 26 | // 每1.5秒发出值 27 | interval(1500), 28 | // 每1秒发出值 29 | interval(1000).pipe(mapTo('1s won!')), 30 | // 每2秒发出值 31 | interval(2000), 32 | // 每2.5秒发出值 33 | interval(2500) 34 | ); 35 | // 输出: "1s won!"..."1s won!"...etc 36 | const subscribe = example.subscribe(val => console.log(val)); 37 | ``` 38 | 39 | ##### 示例 2: 使用 error 进行 race 40 | 41 | ( 42 | [StackBlitz](https://stackblitz.com/edit/typescript-in6fw6?file=index.ts&devtoolsheight=100) 43 | | [jsFiddle](https://jsfiddle.net/gbeL4t55/2/) ) 44 | 45 | ```js 46 | // RxJS v6+ 47 | import { delay, map } from 'rxjs/operators'; 48 | import { of, race } from 'rxjs'; 49 | 50 | // 抛出错误并忽略其他的 observables 。 51 | const first = of('first').pipe( 52 | delay(100), 53 | map(_ => { 54 | throw 'error'; 55 | }) 56 | ); 57 | const second = of('second').pipe(delay(200)); 58 | const third = of('third').pipe(delay(300)); 59 | // nothing logged 60 | race(first, second, third).subscribe(val => console.log(val)); 61 | ``` 62 | 63 | ### 其他资源 64 | 65 | - [race](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-race) :newspaper: - 官方文档 66 | 67 | --- 68 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/race.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/race.ts) 69 | -------------------------------------------------------------------------------- /operators/combination/startwith.md: -------------------------------------------------------------------------------- 1 | # startWith 2 | 3 | #### 函数签名: `startWith(an: Values): Observable` 4 | 5 | ## 发出给定的第一个值 6 | 7 | --- 8 | 9 | :bulb: [BehaviorSubject](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/behaviorsubject.md) 也可以从初始值开始! 10 | 11 | --- 12 | 13 |
14 | 15 | ### 示例 16 | 17 | ( [示例测试](https://github.com/btroncone/learn-rxjs/blob/master/operators/specs/combination/startwith-spec.ts) ) 18 | 19 | ##### 示例 1: 对数字序列使用 startWith 20 | 21 | ( 22 | [StackBlitz](https://stackblitz.com/edit/typescript-2qrwjt?file=index.ts&devtoolsheight=100) 23 | | [jsBin](http://jsbin.com/lezuravizu/1/edit?js,console) | 24 | [jsFiddle](https://jsfiddle.net/btroncone/e8dn3ggp/) ) 25 | 26 | ```js 27 | // RxJS v6+ 28 | import { startWith } from 'rxjs/operators'; 29 | import { of } from 'rxjs'; 30 | 31 | // 发出 (1,2,3) 32 | const source = of(1, 2, 3); 33 | // 从0开始 34 | const example = source.pipe(startWith(0)); 35 | // 输出: 0,1,2,3 36 | const subscribe = example.subscribe(val => console.log(val)); 37 | ``` 38 | 39 | ##### 示例 2: startWith 用作 scan 的初始值 40 | 41 | ( 42 | [StackBlitz](https://stackblitz.com/edit/typescript-8gkbsc?file=index.ts&devtoolsheight=100) 43 | | | [jsBin](http://jsbin.com/gemevuzoha/1/edit?js,console) | 44 | [jsFiddle](https://jsfiddle.net/btroncone/54r3g83e/) ) 45 | 46 | ```js 47 | // RxJS v6+ 48 | import { startWith, scan } from 'rxjs/operators'; 49 | import { of } from 'rxjs'; 50 | 51 | // 发出 ('World!', 'Goodbye', 'World!') 52 | const source = of('World!', 'Goodbye', 'World!'); 53 | // 以 'Hello' 开头,后面接当前字符串 54 | const example = source.pipe( 55 | startWith('Hello'), 56 | scan((acc, curr) => `${acc} ${curr}`) 57 | ); 58 | /* 59 | 输出: 60 | "Hello" 61 | "Hello World!" 62 | "Hello World! Goodbye" 63 | "Hello World! Goodbye World!" 64 | */ 65 | const subscribe = example.subscribe(val => console.log(val)); 66 | ``` 67 | 68 | ##### 示例 3: 使用多个值进行 startWith 69 | 70 | ( 71 | [StackBlitz](https://stackblitz.com/edit/typescript-ek45ff?file=index.ts&devtoolsheight=100) 72 | | [jsBin](http://jsbin.com/cumupemuxa/1/edit?js,console) | 73 | [jsFiddle](https://jsfiddle.net/btroncone/ckcyj3ms/) ) 74 | 75 | ```js 76 | // RxJS v6+ 77 | import { startWith } from 'rxjs/operators'; 78 | import { interval } from 'rxjs'; 79 | 80 | // 每1秒发出值 81 | const source = interval(1000); 82 | // 以 -3, -2, -1 开始 83 | const example = source.pipe(startWith(-3, -2, -1)); 84 | // 输出: -3, -2, -1, 0, 1, 2.... 85 | const subscribe = example.subscribe(val => console.log(val)); 86 | ``` 87 | 88 | ### 相关食谱 89 | 90 | - [智能计数器](../../recipes/smartcounter.md) 91 | 92 | ### 其他资源 93 | 94 | - [startWith](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-startWith) :newspaper: - 官方文档 95 | - [使用 startWith 显示初始值](https://egghead.io/lessons/rxjs-displaying-initial-data-with-startwith?course=step-by-step-async-javascript-with-rxjs) :video_camera: :dollar: - John Linquist 96 | - [当加载时使用 startWith 清除数据](https://egghead.io/lessons/rxjs-reactive-programming-clear-data-while-loading-with-rxjs-startwith?course=introduction-to-reactive-programming) :video_camera: :dollar: - André Staltz 97 | - [组合操作符: concat, startWith](https://egghead.io/lessons/rxjs-combination-operators-concat-startwith?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/startWith.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/startWith.ts) 101 | -------------------------------------------------------------------------------- /operators/combination/withlatestfrom.md: -------------------------------------------------------------------------------- 1 | # withLatestFrom 2 | 3 | #### 函数签名: `withLatestFrom(other: Observable, project: Function): Observable` 4 | 5 | ## 还提供另一个 observable 的最新值。 6 | 7 | --- 8 | 9 | :bulb: 如果你希望每当任意 observable 发出值时各个 observable 的最新值,请尝试 [combinelatest](combinelatest.md)! 10 | 11 | --- 12 | 13 |
14 | 15 | ### 示例 16 | 17 | ##### 示例 1: 发出频率更快的第二个 source 的最新值 18 | 19 | ( 20 | [StackBlitz](https://stackblitz.com/edit/typescript-tznzbj?file=index.ts&devtoolsheight=100) 21 | | [jsBin](http://jsbin.com/fitekeseru/1/edit?js,console) | 22 | [jsFiddle](https://jsfiddle.net/btroncone/9c3pfgpk/) ) 23 | 24 | ```js 25 | // RxJS v6+ 26 | import { withLatestFrom, map } from 'rxjs/operators'; 27 | import { interval } from 'rxjs'; 28 | 29 | // 每5秒发出值 30 | const source = interval(5000); 31 | // 每1秒发出值 32 | const secondSource = interval(1000); 33 | const example = source.pipe( 34 | withLatestFrom(secondSource), 35 | map(([first, second]) => { 36 | return `First Source (5s): ${first} Second Source (1s): ${second}`; 37 | }) 38 | ); 39 | /* 40 | 输出: 41 | "First Source (5s): 0 Second Source (1s): 4" 42 | "First Source (5s): 1 Second Source (1s): 9" 43 | "First Source (5s): 2 Second Source (1s): 14" 44 | ... 45 | */ 46 | const subscribe = example.subscribe(val => console.log(val)); 47 | ``` 48 | 49 | ##### 示例 2: 第二个 source 发出频率更慢一些 50 | 51 | ( 52 | [StackBlitz](https://stackblitz.com/edit/typescript-gigsdv?file=index.ts&devtoolsheight=100) 53 | | [jsBin](http://jsbin.com/vujekucuxa/1/edit?js,console) | 54 | [jsFiddle](https://jsfiddle.net/btroncone/bywLL579/) ) 55 | 56 | ```js 57 | // RxJS v6+ 58 | import { withLatestFrom, map } from 'rxjs/operators'; 59 | import { interval } from 'rxjs'; 60 | 61 | // 每5秒发出值 62 | const source = interval(5000); 63 | // 每1秒发出值 64 | const secondSource = interval(1000); 65 | // withLatestFrom 的 observable 比源 observable 慢 66 | const example = secondSource.pipe( 67 | // 两个 observable 在发出值前要确保至少都有1个值 (需要等待5秒) 68 | withLatestFrom(source), 69 | map(([first, second]) => { 70 | return `Source (1s): ${first} Latest From (5s): ${second}`; 71 | }) 72 | ); 73 | /* 74 | "Source (1s): 4 Latest From (5s): 0" 75 | "Source (1s): 5 Latest From (5s): 0" 76 | "Source (1s): 6 Latest From (5s): 0" 77 | ... 78 | */ 79 | const subscribe = example.subscribe(val => console.log(val)); 80 | ``` 81 | 82 | ### 相关食谱 83 | 84 | - [进度条](../../recipes/progressbar.md) 85 | - [游戏循环](../../recipes/gameloop.md) 86 | 87 | ### 其他资源 88 | 89 | - [withLatestFrom](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-withLatestFrom) :newspaper: - 官方文档 90 | - [组合操作符: withLatestFrom](https://egghead.io/lessons/rxjs-combination-operator-withlatestfrom?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz 91 | 92 | --- 93 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/withLatestFrom.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/withLatestFrom.ts) 94 | -------------------------------------------------------------------------------- /operators/combination/zip.md: -------------------------------------------------------------------------------- 1 | # zip 2 | 3 | #### 函数签名: `zip(observables: *): Observable` 4 | 5 | ### 描述 6 | 7 | ###### TL;DR: 在所有 observables 发出后,将它们的值作为数组发出 8 | 9 | **zip** 操作符会订阅所有内部 observables,然后等待每个发出一个值。一旦发生这种情况,将发出具有相应索引的所有值。这会持续进行,直到至少一个内部 10 | observable 完成。 11 | 12 | --- 13 | 14 | :bulb: 与 [interval](../creation/interval) 或 [timer](../creation/timer.md) 进行组合, zip 可以用来根据另一个 observable 进行定时输出! 15 | 16 | --- 17 | 18 |
19 | 20 | ### 示例 21 | 22 | ##### 示例 1: 以交替的时间间隔对多个 observables 进行 zip 23 | 24 | ( 25 | [StackBlitz](https://stackblitz.com/edit/typescript-5az27c?file=index.ts&devtoolsheight=100) 26 | | [jsBin](http://jsbin.com/lireyisira/1/edit?js,console) | 27 | [jsFiddle](https://jsfiddle.net/btroncone/ton462sg/) ) 28 | 29 | ```js 30 | // RxJS v6+ 31 | import { delay } from 'rxjs/operators'; 32 | import { of, zip } from 'rxjs'; 33 | 34 | const sourceOne = of('Hello'); 35 | const sourceTwo = of('World!'); 36 | const sourceThree = of('Goodbye'); 37 | const sourceFour = of('World!'); 38 | // 一直等到所有 observables 都发出一个值,才将所有值作为数组发出 39 | const example = zip( 40 | sourceOne, 41 | sourceTwo.pipe(delay(1000)), 42 | sourceThree.pipe(delay(2000)), 43 | sourceFour.pipe(delay(3000)) 44 | ); 45 | // 输出: ["Hello", "World!", "Goodbye", "World!"] 46 | const subscribe = example.subscribe(val => console.log(val)); 47 | ``` 48 | 49 | ##### 示例 2: 当一个 observable 完成时进行 zip 50 | 51 | ( 52 | [StackBlitz](https://stackblitz.com/edit/typescript-f4qgry?file=index.ts&devtoolsheight=100) 53 | | [jsBin](http://jsbin.com/fisitatesa/1/edit?js,console) | 54 | [jsFiddle](https://jsfiddle.net/btroncone/oamyk3xr/) ) 55 | 56 | ```js 57 | // RxJS v6+ 58 | import { take } from 'rxjs/operators'; 59 | import { interval, zip } from 'rxjs'; 60 | 61 | // 每1秒发出值 62 | const source = interval(1000); 63 | // 当一个 observable 完成时,便不会再发出更多的值了 64 | const example = zip(source, source.pipe(take(2))); 65 | // 输出: [0,0]...[1,1] 66 | const subscribe = example.subscribe(val => console.log(val)); 67 | ``` 68 | 69 | ### 其他资源 70 | 71 | - [zip](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-zip) :newspaper: - 官方文档 72 | - [组合操作符: zip](https://egghead.io/lessons/rxjs-combination-operator-zip?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz 73 | 74 | --- 75 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/zip.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/zip.ts) 76 | -------------------------------------------------------------------------------- /operators/complete.md: -------------------------------------------------------------------------------- 1 | # 通过示例来学习 RxJS 操作符 2 | 3 | RxJS 操作符的完整列表,每个操作符都有着清晰的解释、相关资源和可执行的示例。 4 | 5 | _[更喜欢按操作符类型进行分类?](README.md)_ 6 | 7 | ### 内容 (按字母顺序) 8 | 9 | - [audit](filtering/audit.md) 10 | - [auditTime](filtering/audittime.md) 11 | - [buffer](transformation/buffer.md) 12 | - [bufferCount](transformation/buffercount.md) 13 | - [bufferTime](transformation/buffertime.md) :star: 14 | - [bufferToggle](transformation/buffertoggle.md) 15 | - [bufferWhen](transformation/bufferwhen.md) 16 | - [catch / catchError](error_handling/catch.md) :star: 17 | - [combineAll](combination/combineall.md) 18 | - [combineLatest](combination/combinelatest.md) :star: 19 | - [concat](combination/concat.md) :star: 20 | - [concatAll](combination/concatall.md) 21 | - [concatMap](transformation/concatmap.md) :star: 22 | - [concatMapTo](transformation/concatmapto.md) 23 | - [create](creation/create.md) 24 | - [debounce](filtering/debounce.md) 25 | - [debounceTime](filtering/debouncetime.md) :star: 26 | - [defaultIfEmpty](conditional/defaultifempty.md) 27 | - [delay](utility/delay.md) 28 | - [delayWhen](utility/delaywhen.md) 29 | - [distinctUntilChanged](filtering/distinctuntilchanged.md) :star: 30 | - [do / tap](utility/do.md) :star: 31 | - [empty](creation/empty.md) 32 | - [every](conditional/every.md) 33 | - [exhaustMap](transformation/exhaustmap.md) 34 | - [expand](transformation/expand.md) 35 | - [filter](filtering/filter.md) :star: 36 | - [finalize / finally](utility/finalize.md) 37 | - [first](filtering/first.md) 38 | - [forkJoin](combination/forkjoin.md) 39 | - [from](creation/from.md) :star: 40 | - [fromEvent](creation/fromevent.md) 41 | - [groupBy](transformation/groupby.md) 42 | - [ignoreElements](filtering/ignoreelements.md) 43 | - [interval](creation/interval.md) 44 | - [last](filtering/last.md) 45 | - [let](utility/let.md) 46 | - [map](transformation/map.md) :star: 47 | - [mapTo](transformation/mapto.md) 48 | - [merge](combination/merge.md) :star: 49 | - [mergeAll](combination/mergeall.md) 50 | - [mergeMap / flatMap](transformation/mergemap.md) :star: 51 | - [multicast](multicasting/multicast.md) 52 | - [of](creation/of.md) :star: 53 | - [partition](transformation/partition.md) 54 | - [pluck](transformation/pluck.md) 55 | - [publish](multicasting/publish.md) 56 | - [race](combination/race.md) 57 | - [range](creation/range.md) 58 | - [retry](error_handling/retry.md) 59 | - [retryWhen](error_handling/retrywhen.md) 60 | - [sample](filtering/sample.md) 61 | - [scan](transformation/scan.md) :star: 62 | - [share](multicasting/share.md) :star: 63 | - [shareReplay](multicasting/sharereplay.md) :star: 64 | - [single](filtering/single.md) 65 | - [skip](filtering/skip.md) 66 | - [skipUntil](filtering/skipuntil.md) 67 | - [skipWhile](filtering/skipwhile.md) 68 | - [startWith](combination/startwith.md) :star: 69 | - [switchMap](transformation/switchmap.md) :star: 70 | - [take](filtering/take.md) :star: 71 | - [takeUntil](filtering/takeuntil.md) :star: 72 | - [takeWhile](filtering/takewhile.md) 73 | - [throttle](filtering/throttle.md) 74 | - [throttleTime](filtering/throttletime.md) 75 | - [throw](creation/throw.md) 76 | - [timeout](utilit/timeout.md) 77 | - [timer](creation/timer.md) 78 | - [toPromise](utility/topromise.md) 79 | - [window](transformation/window.md) 80 | - [windowCount](transformation/windowcount.md) 81 | - [windowTime](transformation/windowtime.md) 82 | - [windowToggle](transformation/windowtoggle.md) 83 | - [windowWhen](transformation/windowwhen.md) 84 | - [withLatestFrom](combination/withlatestfrom.md) :star: 85 | - [zip](combination/zip.md) 86 | 87 | :star: - _常用_ 88 | 89 | ### 其他资源 90 | 91 | * [什么是操作符?](https://cn.rx.js.org/manual/overview.html#29) :newspaper: - 官方文档 92 | * [操作符是什么?](https://egghead.io/lessons/rxjs-what-rxjs-operators-are) :video_camera: :dollar: - André Staltz 93 | -------------------------------------------------------------------------------- /operators/conditional/README.md: -------------------------------------------------------------------------------- 1 | # 条件操作符 2 | 3 | 对于依赖于特定条件的用例,这些操作符可以做到这一点。 4 | 5 | ## 内容 6 | 7 | * [defaultIfEmpty](defaultifempty.md) 8 | * [every](every.md) -------------------------------------------------------------------------------- /operators/conditional/defaultifempty.md: -------------------------------------------------------------------------------- 1 | # defaultIfEmpty 2 | 3 | #### 函数签名: `defaultIfEmpty(defaultValue: any): Observable` 4 | 5 | ## 如果在完成前没有发出任何通知,那么发出给定的值 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 没有值的 Observable.of 的默认值 12 | 13 | ( 14 | [Stackblitz](https://stackblitz.com/edit/typescript-3btzml?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/yawumoqatu/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/8ex96cov/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { defaultIfEmpty } from 'rxjs/operators'; 21 | import { of } from 'rxjs'; 22 | 23 | // 当源 observable 为空时,发出 'Observable.of() Empty!',否则发出源的任意值 24 | const exampleOne = of().pipe(defaultIfEmpty('Observable.of() Empty!')); 25 | // 输出: 'Observable.of() Empty!' 26 | const subscribe = exampleOne.subscribe(val => console.log(val)); 27 | ``` 28 | 29 | ##### 示例 2: Observable.empty 的默认值 30 | 31 | ( 32 | [Stackblitz](https://stackblitz.com/edit/typescript-tyfjhu?file=index.ts&devtoolsheight=100) 33 | | [jsBin](http://jsbin.com/kojafuvesu/1/edit?js,console) | 34 | [jsFiddle](https://jsfiddle.net/btroncone/3edw828p/) ) 35 | 36 | ```js 37 | // RxJS v6+ 38 | import { defaultIfEmpty } from 'rxjs/operators'; 39 | import { empty } from 'rxjs'; 40 | 41 | // 当源 observable 为空时,发出 'Observable.empty()!',否则发出源的任意值 42 | const example = empty().pipe(defaultIfEmpty('Observable.empty()!')); 43 | // 输出: 'Observable.empty()!' 44 | const subscribe = example.subscribe(val => console.log(val)); 45 | ``` 46 | 47 | ### 其他资源 48 | 49 | - [defaultIfEmpty](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-defaultIfEmpty) :newspaper: - 官方文档 50 | 51 | --- 52 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/defaultIfEmpty.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/defaultIfEmpty.ts) 53 | -------------------------------------------------------------------------------- /operators/conditional/every.md: -------------------------------------------------------------------------------- 1 | # every 2 | 3 | #### 函数签名: `every(predicate: function, thisArg: any): Observable` 4 | 5 | ## 如果完成时所有的值都能通过断言,那么发出 true,否则发出 false 。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 一些值不符合条件 12 | 13 | ( 14 | [Stackblitz](https://stackblitz.com/edit/typescript-299d7s?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/cibijotase/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/1b46tsm7/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { every } from 'rxjs/operators'; 21 | import { of } from 'rxjs'; 22 | 23 | // 发出5个值 24 | const source = of(1, 2, 3, 4, 5); 25 | const example = source.pipe( 26 | // 每个值都是偶数吗? 27 | every(val => val % 2 === 0) 28 | ); 29 | // 输出: false 30 | const subscribe = example.subscribe(val => console.log(val)); 31 | ``` 32 | 33 | ##### 示例 2: 所有值都符合条件 34 | 35 | ( 36 | [Stackblitz](https://stackblitz.com/edit/typescript-ztrzqe?file=index.ts&devtoolsheight=100) 37 | | [jsBin](http://jsbin.com/yuxefiviko/1/edit?js,console) | 38 | [jsFiddle](https://jsfiddle.net/btroncone/x34nLmcj/) ) 39 | 40 | ```js 41 | // RxJS v6+ 42 | import { every } from 'rxjs/operators'; 43 | import { of } from 'rxjs'; 44 | 45 | // 发出5个值 46 | const allEvens = of(2, 4, 6, 8, 10); 47 | const example = allEvens.pipe( 48 | // 每个值都是偶数吗? 49 | every(val => val % 2 === 0) 50 | ); 51 | // 输出: true 52 | const subscribe = example.subscribe(val => console.log(val)); 53 | ``` 54 | 55 | ### 其他资源 56 | 57 | - [every](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-every) :newspaper: - 官方文档 58 | 59 | --- 60 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/every.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/every.ts) 61 | -------------------------------------------------------------------------------- /operators/creation/README.md: -------------------------------------------------------------------------------- 1 | # 创建操作符 2 | 3 | 这些运算符几乎允许你基于任何东西来创建一个 observable 。从一般用例到特殊用例,你都可以做到, 4 | 并且鼓励[将一切转换成流](http://slides.com/robwormald/everything-is-a-stream#/)。 5 | 6 | ## 内容 7 | 8 | - [create](create.md) 9 | - [empty](empty.md) 10 | - [from](from.md) :star: 11 | - [fromEvent](fromevent.md) 12 | - [fromPromise](frompromise.md) :star: 13 | - [interval](interval.md) 14 | - [of](of.md) :star: 15 | - [range](range.md) 16 | - [throw](throw.md) 17 | - [timer](timer.md) 18 | 19 | :star: - *常用* 20 | 21 | ### 其他资源 22 | 23 | - [从零开始创建 Observables](https://egghead.io/courses/rxjs-beyond-the-basics-creating-observables-from-scratch) :video_camera: :dollar: - André Staltz 24 | -------------------------------------------------------------------------------- /operators/creation/create.md: -------------------------------------------------------------------------------- 1 | # create 2 | 3 | #### 函数签名: `create(subscribe: function)` 4 | 5 | ## 使用给定的订阅函数来创建 observable 。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 发出多个值的 observable 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-baxh98?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/qorugiwaba/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/td5107he/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { Observable } from 'rxjs'; 21 | /* 22 | 创建在订阅函数中发出 'Hello' 和 'World' 的 observable 。 23 | */ 24 | const hello = Observable.create(function(observer) { 25 | observer.next('Hello'); 26 | observer.next('World'); 27 | }); 28 | 29 | // 输出: 'Hello'...'World' 30 | const subscribe = hello.subscribe(val => console.log(val)); 31 | ``` 32 | 33 | ##### 示例 2: 基于定时器发出偶数的 observable 34 | 35 | ( 36 | [StackBlitz](https://stackblitz.com/edit/typescript-xvezxn?file=index.ts&devtoolsheight=100) 37 | | [jsBin](http://jsbin.com/lodilohate/1/edit?js,console) | 38 | [jsFiddle](https://jsfiddle.net/btroncone/vtozg6uf/) ) 39 | 40 | ```js 41 | // RxJS v6+ 42 | import { Observable } from 'rxjs'; 43 | 44 | /* 45 | 每秒自增值并且只发出偶数 46 | */ 47 | const evenNumbers = Observable.create(function(observer) { 48 | let value = 0; 49 | const interval = setInterval(() => { 50 | if (value % 2 === 0) { 51 | observer.next(value); 52 | } 53 | value++; 54 | }, 1000); 55 | 56 | return () => clearInterval(interval); 57 | }); 58 | // 输出: 0...2...4...6...8 59 | const subscribe = evenNumbers.subscribe(val => console.log(val)); 60 | // 10秒后取消订阅 61 | setTimeout(() => { 62 | subscribe.unsubscribe(); 63 | }, 10000); 64 | ``` 65 | 66 | 67 | ### 其他资源 68 | 69 | - [create](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-create) :newspaper: - 官方文档 70 | - [创建操作符: Create()](https://egghead.io/lessons/rxjs-creation-operator-create?course=rxjs-beyond-the-basics-creating-observables-from-scratch) :video_camera: :dollar: - André Staltz 71 | - [使用 Observable.create 来实现细粒度的控制](https://egghead.io/lessons/rxjs-using-observable-create-for-fine-grained-control) :video_camera: :dollar: - Shane Osbourne 72 | 73 | --- 74 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/GenerateObservable.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/GenerateObservable.ts) 75 | -------------------------------------------------------------------------------- /operators/creation/empty.md: -------------------------------------------------------------------------------- 1 | # empty 2 | 3 | #### 函数签名: `empty(scheduler: Scheduler): Observable` 4 | 5 | ## 立即完成的 observable 。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: empty 会立即完成 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-aqfpkq?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/rodubucaqa/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/bz71mzuy/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { empty } from 'rxjs'; 21 | 22 | // 输出: 'Complete!' 23 | const subscribe = empty().subscribe({ 24 | next: () => console.log('Next'), 25 | complete: () => console.log('Complete!') 26 | }); 27 | ``` 28 | 29 | ##### 示例 2: 使用定时器的 `empty` 30 | 31 | ( 32 | [StackBlitz](https://stackblitz.com/edit/typescript-uujo8t?file=index.ts&devtoolsheight=50) 33 | ) 34 | 35 | ```js 36 | // RxJS v6+ 37 | import { interval, fromEvent, merge, empty } from 'rxjs'; 38 | import { switchMap, scan, takeWhile, startWith, mapTo } from 'rxjs/operators'; 39 | 40 | const countdownSeconds = 10; 41 | const setHTML = id => val => (document.getElementById(id).innerHTML = val); 42 | const pauseButton = document.getElementById('pause'); 43 | const resumeButton = document.getElementById('resume'); 44 | const interval$ = interval(1000).pipe(mapTo(-1)); 45 | 46 | const pause$ = fromEvent(pauseButton, 'click').pipe(mapTo(false)); 47 | const resume$ = fromEvent(resumeButton, 'click').pipe(mapTo(true)); 48 | 49 | const timer$ = merge(pause$, resume$) 50 | .pipe( 51 | startWith(true), 52 | // 如果定时器暂停,则返回空的 Observable 53 | switchMap(val => (val ? interval$ : empty())), 54 | scan((acc, curr) => (curr ? curr + acc : acc), countdownSeconds), 55 | takeWhile(v => v >= 0) 56 | ) 57 | .subscribe(setHTML('remaining')); 58 | ``` 59 | 60 | ### 其他资源 61 | 62 | - [empty](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-empty) :newspaper: - 官方文档 63 | - [Creation operators: empty, never, and throw](https://egghead.io/lessons/rxjs-creation-operators-empty-never-throw?course=rxjs-beyond-the-basics-creating-observables-from-scratch) :video_camera: :dollar: - André Staltz 64 | 65 | --- 66 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/EmptyObservable.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/EmptyObservable.ts) 67 | -------------------------------------------------------------------------------- /operators/creation/from.md: -------------------------------------------------------------------------------- 1 | # from 2 | 3 | #### 函数签名: `from(ish: ObservableInput, mapFn: function, thisArg: any, scheduler: Scheduler): Observable` 4 | 5 | ## 将数组、promise 或迭代器转换成 observable 。 6 | 7 | --- 8 | 9 | :bulb: 对于数组和迭代器,所有包含的值都会被作为序列发出! 10 | 11 | :bulb: 此操作符也可以用来将字符串作为字符的序列发出! 12 | 13 | --- 14 | 15 |
16 | 17 | ### 示例 18 | 19 | ##### 示例 1: 数组转换而来的 observable 20 | 21 | ( 22 | [StackBlitz](https://stackblitz.com/edit/typescript-sckwsw?file=index.ts&devtoolsheight=100) 23 | | [jsBin](http://jsbin.com/foceyuketi/1/edit?js,console) | 24 | [jsFiddle](https://jsfiddle.net/btroncone/o7kb5e6j/) ) 25 | 26 | ```js 27 | // RxJS v6+ 28 | import { from } from 'rxjs'; 29 | 30 | // 将数组作为值的序列发出 31 | const arraySource = from([1, 2, 3, 4, 5]); 32 | // 输出: 1,2,3,4,5 33 | const subscribe = arraySource.subscribe(val => console.log(val)); 34 | ``` 35 | 36 | ##### 示例 2: promise 转换而来的 observable 37 | 38 | ( 39 | [StackBlitz](https://stackblitz.com/edit/typescript-clpg1f?file=index.ts&devtoolsheight=100) 40 | | [jsBin](http://jsbin.com/tamofinujo/1/edit?js,console) | 41 | [jsFiddle](https://jsfiddle.net/btroncone/2czc5sae/) ) 42 | 43 | ```js 44 | // RxJS v6+ 45 | import { from } from 'rxjs'; 46 | 47 | // 发出 promise 的结果 48 | const promiseSource = from(new Promise(resolve => resolve('Hello World!'))); 49 | // 输出: 'Hello World' 50 | const subscribe = promiseSource.subscribe(val => console.log(val)); 51 | ``` 52 | 53 | ##### 示例 3: 集合转换而来的 observable 54 | 55 | ( 56 | [StackBlitz](https://stackblitz.com/edit/typescript-drfckx?file=index.ts&devtoolsheight=100) 57 | | [jsBin](http://jsbin.com/tezohobudu/1/edit?js,console) | 58 | [jsFiddle](https://jsfiddle.net/btroncone/ae6hu9a8/) ) 59 | 60 | ```js 61 | // RxJS v6+ 62 | import { from } from 'rxjs'; 63 | 64 | // 使用 js 的集合 65 | const map = new Map(); 66 | map.set(1, 'Hi'); 67 | map.set(2, 'Bye'); 68 | 69 | const mapSource = from(map); 70 | // 输出: [1, 'Hi'], [2, 'Bye'] 71 | const subscribe = mapSource.subscribe(val => console.log(val)); 72 | ``` 73 | 74 | ##### 示例 4: 字符串转换而来的 observable 75 | 76 | ( 77 | [StackBlitz](https://stackblitz.com/edit/typescript-19nejh?file=index.ts&devtoolsheight=100) 78 | | [jsBin](http://jsbin.com/wenozubana/1/edit?js,console) | 79 | [jsFiddle](https://jsfiddle.net/btroncone/hfvzjcvL/) ) 80 | 81 | ```js 82 | // RxJS v6+ 83 | import { from } from 'rxjs'; 84 | 85 | // 将字符串作为字符序列发出 86 | const source = from('Hello World'); 87 | // 输出: 'H','e','l','l','o',' ','W','o','r','l','d' 88 | const subscribe = source.subscribe(val => console.log(val)); 89 | ``` 90 | 91 | ### 相关食谱 92 | 93 | - [进度条](../../recipes/progressbar.md) 94 | - [HTTP 轮询](../../recipes/http-polling.md) 95 | 96 | ### 其他资源 97 | 98 | - [from](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-from) :newspaper: - 官方文档 99 | - [创建操作符: from, fromArray, fromPromise](https://egghead.io/lessons/rxjs-creation-operators-from-fromarray-frompromise?course=rxjs-beyond-the-basics-creating-observables-from-scratch) :video_camera: :dollar: - André Staltz 100 | 101 | --- 102 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/from.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/from.ts) 103 | -------------------------------------------------------------------------------- /operators/creation/fromevent.md: -------------------------------------------------------------------------------- 1 | # fromEvent 2 | 3 | #### 函数签名: `fromEvent(target: EventTargetLike, eventName: string, selector: function): Observable` 4 | 5 | ## 将事件转换成 observable 序列。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 鼠标事件转换而来的 observable 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-mfyefr?file=index.ts&devtoolsheight=50) 15 | | [jsBin](http://jsbin.com/xikapewoqa/1/edit?js,console,output) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/vbLz1pdx/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { fromEvent } from 'rxjs'; 21 | import { map } from 'rxjs/operators'; 22 | 23 | // 创建发出点击事件的 observable 24 | const source = fromEvent(document, 'click'); 25 | // 映射成给定的事件时间戳 26 | const example = source.pipe(map(event => `Event time: ${event.timeStamp}`)); 27 | // 输出 (示例中的数字以运行时为准): 'Event time: 7276.390000000001' 28 | const subscribe = example.subscribe(val => console.log(val)); 29 | ``` 30 | 31 | ### 相关食谱 32 | 33 | - [智能计数器](../../recipes/smartcounter.md) 34 | - [进度条](../../recipes/progressbar.md) 35 | - [游戏循环](../../recipes/gameloop.md) 36 | - [HTTP 轮询](../../recipes/http-polling.md) 37 | 38 | ### 其他资源 39 | 40 | - [fromEvent](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-fromEvent) :newspaper: - 官方文档 41 | 42 | --- 43 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/observable/FromEventObservable.ts](https://github.com/ReactiveX/rxjs/blob/master/src/observable/FromEventObservable.ts) 44 | -------------------------------------------------------------------------------- /operators/creation/frompromise.md: -------------------------------------------------------------------------------- 1 | # fromPromise 2 | 3 | #### 函数签名: `fromPromise(promise: Promise, scheduler: Scheduler): Observable` 4 | 5 | ## 创建由 promise 转换而来的 observable,并发出 promise 的结果。 6 | 7 | --- 8 | 9 | :bulb: 打平类操作符可以接收 promises 而不需要 observable 包装! 10 | 11 | :bulb: 你还可以使用 [from](from.md) 达到同样的效果! 12 | 13 | --- 14 | 15 |
16 | 17 | ### 示例 18 | 19 | ##### 示例 1: 将 promise 转换成 observable 并捕获错误 20 | 21 | ( [jsBin](http://jsbin.com/cokivecima/1/edit?js,console) | 22 | [jsFiddle](https://jsfiddle.net/btroncone/upy6nr6n/) ) 23 | 24 | ```js 25 | import { of } from 'rxjs/observable/of'; 26 | import { fromPromise } from 'rxjs/observable/fromPromise'; 27 | import { mergeMap, catchError } from 'rxjs/operators'; 28 | 29 | // 基于输入来决定是 resolve 还是 reject 的示例 promise 30 | const myPromise = willReject => { 31 | return new Promise((resolve, reject) => { 32 | if (willReject) { 33 | reject('Rejected!'); 34 | } 35 | resolve('Resolved!'); 36 | }); 37 | }; 38 | // 先发出 true,然后是 false 39 | const source = of(true, false); 40 | const example = source.pipe( 41 | mergeMap(val => 42 | fromPromise(myPromise(val)).pipe( 43 | // 捕获并优雅地处理 reject 的结果 44 | catchError(error => of(`Error: ${error}`)) 45 | ) 46 | ) 47 | ); 48 | // 输出: 'Error: Rejected!', 'Resolved!' 49 | const subscribe = example.subscribe(val => console.log(val)); 50 | ``` 51 | 52 | ### 其他资源 53 | 54 | * [fromPromise](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-fromPromise) :newspaper: - 官方文档 55 | * [创建操作符: from, fromArray, fromPromise](https://egghead.io/lessons/rxjs-creation-operators-from-fromarray-frompromise?course=rxjs-beyond-the-basics-creating-observables-from-scratch) :video_camera: :dollar: - André Staltz 56 | * [fromPromise - 指南](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/promises.md) 57 | 58 | --- 59 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/fromPromise.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/fromPromise.ts) 60 | -------------------------------------------------------------------------------- /operators/creation/interval.md: -------------------------------------------------------------------------------- 1 | # interval 2 | 3 | #### 函数签名: `interval(period: number, scheduler: Scheduler): Observable` 4 | 5 | ## 基于给定时间间隔发出数字序列。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 每秒发出自增数字 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-ohddud?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/vigohomabo/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/x3mrwzr0/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { interval } from 'rxjs'; 21 | 22 | // 每1秒发出数字序列中的值 23 | const source = interval(1000); 24 | // 数字: 0,1,2,3,4,5.... 25 | const subscribe = source.subscribe(val => console.log(val)); 26 | ``` 27 | 28 | ### 其他资源 29 | 30 | - [interval](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-interval) :newspaper: - 官方文档 31 | - [创建操作符: interval 和 timer](https://egghead.io/lessons/rxjs-creation-operators-interval-and-timer?course=rxjs-beyond-the-basics-creating-observables-from-scratch) :video_camera: :dollar: - André Staltz 32 | 33 | --- 34 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/interval.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/interval.ts) 35 | -------------------------------------------------------------------------------- /operators/creation/of.md: -------------------------------------------------------------------------------- 1 | # of / just 2 | 3 | #### 函数签名: ` of(...values, scheduler: Scheduler): Observable` 4 | 5 | ## 按顺序发出任意数量的值。 6 | 7 | ### 示例 8 | 9 | ##### 示例 1: 发出数字序列 10 | 11 | ( 12 | [StackBlitz](https://stackblitz.com/edit/typescript-kbpvmm?file=index.ts&devtoolsheight=100) 13 | | [jsBin](http://jsbin.com/kodixitoji/1/edit?js,console) | 14 | [jsFiddle](https://jsfiddle.net/btroncone/f7b35ayz/) ) 15 | 16 | ```js 17 | // RxJS v6+ 18 | import { of } from 'rxjs'; 19 | // 依次发出提供的任意数量的值 20 | const source = of(1, 2, 3, 4, 5); 21 | // 输出: 1,2,3,4,5 22 | const subscribe = source.subscribe(val => console.log(val)); 23 | ``` 24 | 25 | ##### 示例 2: 发出对象、数组和函数 26 | 27 | ( 28 | [StackBlitz](https://stackblitz.com/edit/typescript-m1jbw9?file=index.ts&devtoolsheight=100) 29 | | [jsBin](http://jsbin.com/xevobujama/1/edit?js,console) | 30 | [jsFiddle](https://jsfiddle.net/btroncone/d9rng4dj/) ) 31 | 32 | ```js 33 | // RxJS v6+ 34 | import { of } from 'rxjs'; 35 | // 出任意类型值 36 | const source = of({ name: 'Brian' }, [1, 2, 3], function hello() { 37 | return 'Hello'; 38 | }); 39 | // 输出: {name: 'Brian}, [1,2,3], function hello() { return 'Hello' } 40 | const subscribe = source.subscribe(val => console.log(val)); 41 | ``` 42 | 43 | 44 | ### 其他资源 45 | 46 | - [of](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-of) :newspaper: - 官方文档 47 | - [创建操作符: of](https://egghead.io/lessons/rxjs-creation-operator-of?course=rxjs-beyond-the-basics-creating-observables-from-scratch) :video_camera: :dollar: - André Staltz 48 | 49 | > :file_folder: 源码: 50 | > [https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/of.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/of.ts) 51 | -------------------------------------------------------------------------------- /operators/creation/range.md: -------------------------------------------------------------------------------- 1 | # range 2 | 3 | #### 函数签名: `range(start: number, count: number, scheduler: Scheduler): Observable` 4 | 5 | ## 依次发出给定区间内的数字。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 发出1-10的区间值 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-r5zrww?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/yalefomage/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/cfvfgwn9/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { range } from 'rxjs'; 21 | 22 | // 依次发出1-10 23 | const source = range(1, 10); 24 | // 输出: 1,2,3,4,5,6,7,8,9,10 25 | const example = source.subscribe(val => console.log(val)); 26 | ``` 27 | 28 | ### 其他资源 29 | 30 | - [range](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-range) :newspaper: - 官方文档 31 | 32 | --- 33 | 34 | > :file_folder: 源码: 35 | > [https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/range.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/range.ts) 36 | -------------------------------------------------------------------------------- /operators/creation/throw.md: -------------------------------------------------------------------------------- 1 | # throw 2 | 3 | #### 函数签名: `throw(error: any, scheduler: Scheduler): Observable` 4 | 5 | ## 在订阅上发出错误 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 在订阅上抛出错误 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-5d3stz?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/punubequju/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/mks82xqz/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { throwError } from 'rxjs'; 21 | 22 | // 在订阅上使用指定值来发出错误 23 | const source = throwError('This is an error!'); 24 | // 输出: 'Error: This is an error!' 25 | const subscribe = source.subscribe({ 26 | next: val => console.log(val), 27 | complete: () => console.log('Complete!'), 28 | error: val => console.log(`Error: ${val}`) 29 | }); 30 | ``` 31 | 32 | ### 相关示例 33 | 34 | - [3次重试后抛出错误](../error_handling/retrywhen.md) 35 | 36 | ### 其他资源 37 | 38 | ### 其他资源 39 | 40 | - [throw](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-throw) :newspaper: - 官方文档 41 | - [创建操作符: empty, never 和 throw](https://egghead.io/lessons/rxjs-creation-operators-empty-never-throw?course=rxjs-beyond-the-basics-creating-observables-from-scratch) :video_camera: :dollar: - André Staltz 42 | 43 | > :file_folder: 源码: 44 | > [https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/throwError.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/throwError.ts) 45 | -------------------------------------------------------------------------------- /operators/creation/timer.md: -------------------------------------------------------------------------------- 1 | # timer 2 | 3 | #### 函数签名: `timer(initialDelay: number | Date, period: number, scheduler: Scheduler): Observable` 4 | 5 | ## 给定持续时间后,再按照指定间隔时间依次发出数字。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 当 timer 结束时发出一个值 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-fvkzgg?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/pazajanehu/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/vpx0y8fu/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { timer } from 'rxjs'; 21 | 22 | // 1秒后发出0,然后结束,因为没有提供第二个参数 23 | const source = timer(1000); 24 | // 输出: 0 25 | const subscribe = source.subscribe(val => console.log(val)); 26 | ``` 27 | 28 | ##### 示例 2: timer 1秒后发出值,然后每2秒发出值 29 | 30 | ( 31 | [StackBlitz](https://stackblitz.com/edit/typescript-h9pzxr?file=index.ts&devtoolsheight=100) 32 | | [jsBin](http://jsbin.com/kejidofuje/1/edit?js,console) | 33 | [jsFiddle](https://jsfiddle.net/btroncone/30ddov8j/) ) 34 | 35 | ```js 36 | // RxJS v6+ 37 | import { timer } from 'rxjs'; 38 | 39 | /* 40 | timer 接收第二个参数,它决定了发出序列值的频率,在本例中我们在1秒发出第一个值, 41 | 然后每2秒发出序列值 42 | */ 43 | const source = timer(1000, 2000); 44 | // 输出: 0,1,2,3,4,5...... 45 | const subscribe = source.subscribe(val => console.log(val)); 46 | ``` 47 | 48 | ### 相关食谱 49 | 50 | - [HTTP 轮询](../../recipes/http-polling.md) 51 | 52 | ### 其他资源 53 | 54 | - [timer](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#static-method-timer) :newspaper: - 官方文档 55 | - [创建操作符: interval 和 timer](https://egghead.io/lessons/rxjs-creation-operators-interval-and-timer?course=rxjs-beyond-the-basics-creating-observables-from-scratch) :video_camera: :dollar: - André Staltz 56 | 57 | > :file_folder: 源码: 58 | > [https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/timer.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/timer.ts) 59 | -------------------------------------------------------------------------------- /operators/error_handling/README.md: -------------------------------------------------------------------------------- 1 | # 错误处理操作符 2 | 3 | 错误是开发中不幸的副作用。这些操作符提供了一些高效的方式来优雅地处理错误并且在它们应该发生的情况下重试逻辑, 4 | 5 | ## 内容 6 | 7 | * [catch / catchError](catch.md) :star: 8 | * [retry](retry.md) 9 | * [retryWhen](retrywhen.md) 10 | 11 | :star: - *常用* 12 | -------------------------------------------------------------------------------- /operators/error_handling/catch.md: -------------------------------------------------------------------------------- 1 | # catch / catchError 2 | 3 | #### 函数签名: `catchError(project : function): Observable` 4 | 5 | ## 优雅地处理 observable 序列中的错误 6 | 7 | --- 8 | 9 | :warning: 记住要在 catch 函数中返回一个 observable ! 10 | 11 | --- 12 | 13 |
14 | 15 | ### 示例 16 | 17 | ( [示例测试](https://github.com/btroncone/learn-rxjs/blob/master/operators/specs/error_handling/catch-spec.ts) ) 18 | 19 | ##### 示例 1: 捕获 observable 中的错误 20 | 21 | ( 22 | [StackBlitz](https://stackblitz.com/edit/typescript-auc2u2?file=index.ts&devtoolsheight=100) 23 | | [jsBin](http://jsbin.com/porevoxelu/1/edit?js,console) | 24 | [jsFiddle](https://jsfiddle.net/btroncone/wk4oLLqc/) ) 25 | 26 | ```js 27 | // RxJS v6+ 28 | import { throwError, of } from 'rxjs'; 29 | import { catchError } from 'rxjs/operators'; 30 | // 发出错误 31 | const source = throwError('This is an error!'); 32 | // 优雅地处理错误,并返回带有错误信息的 observable 33 | const example = source.pipe(catchError(val => of(`I caught: ${val}`))); 34 | // 输出: 'I caught: This is an error' 35 | const subscribe = example.subscribe(val => console.log(val)); 36 | ``` 37 | 38 | ##### 示例 2: 捕获拒绝的 promise 39 | 40 | ( 41 | [StackBlitz](https://stackblitz.com/edit/typescript-nte3xs?file=index.ts&devtoolsheight=100) 42 | | [jsBin](http://jsbin.com/rusaxubanu/1/edit?js,console) | 43 | [jsFiddle](https://jsfiddle.net/btroncone/sLq92gLv/) ) 44 | 45 | ```js 46 | // RxJS v6+ 47 | import { timer, from, of } from 'rxjs'; 48 | import { mergeMap, catchError } from 'rxjs/operators'; 49 | 50 | // 创建立即拒绝的 Promise 51 | const myBadPromise = () => 52 | new Promise((resolve, reject) => reject('Rejected!')); 53 | // 1秒后发出单个值 54 | const source = timer(1000); 55 | // 捕获拒绝的 promise,并返回包含错误信息的 observable 56 | const example = source.pipe( 57 | mergeMap(_ => 58 | from(myBadPromise()).pipe(catchError(error => of(`Bad Promise: ${error}`))) 59 | ) 60 | ); 61 | // 输出: 'Bad Promise: Rejected' 62 | const subscribe = example.subscribe(val => console.log(val)); 63 | ``` 64 | 65 | ### 其他资源 66 | 67 | - [错误处理操作符: catch](https://egghead.io/lessons/rxjs-error-handling-operator-catch?course=rxjs-beyond-the-basics-operators-in-depth) :video_camera: :dollar: - André Staltz 68 | 69 | --- 70 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/catch.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/catch.ts) 71 | -------------------------------------------------------------------------------- /operators/error_handling/retry.md: -------------------------------------------------------------------------------- 1 | # retry 2 | 3 | #### 函数签名: `retry(number: number): Observable` 4 | 5 | ## 如果发生错误,以指定次数重试 observable 序列。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 出错的话可以重试2次 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-jpjcpg?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/yovacuxuqa/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/hg7z16bo/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { interval, of, throwError } from 'rxjs'; 21 | import { mergeMap, retry } from 'rxjs/operators'; 22 | 23 | // 每1秒发出值 24 | const source = interval(1000); 25 | const example = source.pipe( 26 | mergeMap(val => { 27 | // 抛出错误以进行演示 28 | if (val > 5) { 29 | return throwError('Error!'); 30 | } 31 | return of(val); 32 | }), 33 | // 出错的话可以重试2次 34 | retry(2) 35 | ); 36 | /* 37 | 输出: 38 | 0..1..2..3..4..5.. 39 | 0..1..2..3..4..5.. 40 | 0..1..2..3..4..5.. 41 | "Error!: Retried 2 times then quit!" 42 | */ 43 | const subscribe = example.subscribe({ 44 | next: val => console.log(val), 45 | error: val => console.log(`${val}: Retried 2 times then quit!`) 46 | }); 47 | ``` 48 | 49 | ### 其他资源 50 | 51 | - [retry](https://cn.rx.js.org/class/es6/Observable.js~Observable.html#instance-method-retry) :newspaper: - 官方文档 52 | - [错误处理操作符: 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 53 | 54 | --- 55 | > :file_folder: 源码: [https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/retry.ts](https://github.com/ReactiveX/rxjs/blob/master/src/internal/operators/retry.ts) 56 | -------------------------------------------------------------------------------- /operators/error_handling/retrywhen.md: -------------------------------------------------------------------------------- 1 | # retryWhen 2 | 3 | #### 函数签名: `retryWhen(receives: (errors: Observable) => Observable, the: scheduler): Observable` 4 | 5 | ## 当发生错误时,基于自定义的标准来重试 observable 序列。 6 | 7 |
8 | 9 | ### 示例 10 | 11 | ##### 示例 1: 在指定的时间间隔后触发重试 12 | 13 | ( 14 | [StackBlitz](https://stackblitz.com/edit/typescript-zpbsw6?file=index.ts&devtoolsheight=100) 15 | | [jsBin](http://jsbin.com/miduqexalo/1/edit?js,console) | 16 | [jsFiddle](https://jsfiddle.net/btroncone/49mkhsyr/) ) 17 | 18 | ```js 19 | // RxJS v6+ 20 | import { timer, interval } from 'rxjs'; 21 | import { map, tap, retryWhen, delayWhen } from 'rxjs/operators'; 22 | 23 | // 每1秒发出值 24 | const source = interval(1000); 25 | const example = source.pipe( 26 | map(val => { 27 | if (val > 5) { 28 | // 错误将由 retryWhen 接收 29 | throw val; 30 | } 31 | return val; 32 | }), 33 | retryWhen(errors => 34 | errors.pipe( 35 | // 输出错误信息 36 | tap(val => console.log(`Value ${val} was too high!`)), 37 | // 5秒后重启 38 | delayWhen(val => timer(val * 1000)) 39 | ) 40 | ) 41 | ); 42 | /* 43 | 输出: 44 | 0 45 | 1 46 | 2 47 | 3 48 | 4 49 | 5 50 | "Value 6 was too high!" 51 | --等待5秒后然后重复此过程 52 | */ 53 | const subscribe = example.subscribe(val => console.log(val)); 54 | ``` 55 | 56 | ##### 示例 2: 时间间隔增加的自定义重试 57 | 58 | ( 59 | [StackBlitz](https://stackblitz.com/edit/angular-cwnknr?file=app%2Frxjs-utils.ts) 60 | ) 61 | 62 | ```js 63 | import { Observable, _throw, timer } from 'rxjs'; 64 | import { mergeMap, finalize } from 'rxjs/operators'; 65 | 66 | export const genericRetryStrategy = ({ 67 | maxRetryAttempts = 3, 68 | scalingDuration = 1000, 69 | excludedStatusCodes = [] 70 | }: { 71 | maxRetryAttempts?: number, 72 | scalingDuration?: number, 73 | excludedStatusCodes?: number[] 74 | } = {}) => (attempts: Observable) => { 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 |
92 |
93 |
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 | --------------------------------------------------------------------------------