diff --git a/.travis.yml b/.travis.yml index 74a961ef0a..fb5434fd15 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,29 +17,21 @@ env: global: - secure: "Qdr/ZwB7Tv25n7YQfudtGcnazeE7l4UM7QaoMxav6GdpFvSqaPwXJsjBFBogMAw5n1jvYM43Ah3JheChpx8yodhwgc4tAgqC1KJd4THmjq9keQMglN23QXCW2gq9/TGEZsQi96kUs/gpHmq1SXPVMil+eCKYaTtydtSeEgHnZhSagKbWln8BWi0mNn3l8MFJ5mIHz/uzyHolE57olfHCA7c+E3rw08O7EbEwQUkID2pYcIgC6Bm6dFHwgjcSJ9py8d6HOprhxAiTvAL8B97y0CBufo9SmpO7AqAgejphIESevscLiiKtP+sjctPXkS3YI28L5tCuOJulfgQKlMPfdDFBPnIu/HfN9irXK00N5ZQBtQ0596VQ6qGpx+yS7MXWYLP67+j95nbjN4gouRI4NXovMfbNPQ8oVrXjQLM52xCi2eA3W9p3plnqv544bdfjctOhGaBzW5A/1rSC7MHsAn6R0l9B0GLR60mJPw9oBko8OC/1uhWYqmzHzWoC8fzHi2QaynD9pxaYi3Gb9r1urLs8a3paWmaotrcXiPAvMK3PiC9o+/Osdp85JROnhFMyS0hhMz0PgwIB2+q9Fbv0vhjGSkW/6pgZWDx7k79OhPQQsiBDH23NMznWWPGzuXvTcACTnUynMz4G7cIkebOx6JVDBjwusKD2/+PE9cLs06w=" matrix: - - NODE_VER=8 FULL_VALIDATE=true - - NODE_VER=8 TSC_VERSION=2.8.* - - NODE_VER=8 TSC_VERSION=next - - NODE_VER=9 - - NODE_VER=9 TSC_VERSION=2.8.* - - NODE_VER=9 TSC_VERSION=next - - NODE_VER=10 - NODE_VER=10 TSC_VERSION=2.8.* - NODE_VER=10 TSC_VERSION=next + - NODE_VER=11 FULL_VALIDATE=true TSC_VERSION=2.8.* + - NODE_VER=11 TSC_VERSION=next matrix: fast_finish: true allow_failures: - - NODE_VER=8 TSC_VERSION=2.8.* - - NODE_VER=8 TSC_VERSION=next - - NODE_VER=9 TSC_VERSION=2.8.* - - NODE_VER=9 TSC_VERSION=next - NODE_VER=10 TSC_VERSION=2.8.* - NODE_VER=10 TSC_VERSION=next + - NODE_VER=11 TSC_VERSION=next before_install: - nvm install $NODE_VER - - npm install -g npm@6 && node -v && npm -v + - npm install -g npm && node -v && npm -v install: - npm ci @@ -55,4 +47,4 @@ script: after_success: - if [ "$FULL_VALIDATE" == "true" ]; then npm run test:cover && npx nyc report --reporter=text-lcov | npx coveralls; fi - - if [ "$FULL_VALIDATE" == "true" ]; then npm run tests2png && cd docs_app && chmod 755 scripts/deploy-to-firebase.sh && npm run deploy-production; fi \ No newline at end of file + - if [ "$FULL_VALIDATE" == "true" ]; then npm run tests2png && cd docs_app && chmod 755 scripts/deploy-to-firebase.sh && npm run deploy-production; fi diff --git a/CHANGELOG.md b/CHANGELOG.md index eb7f394daf..da84129559 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +# [6.5.0](https://github.com/reactivex/rxjs/compare/6.4.0...6.5.0) (2019-04-23) + + +### Bug Fixes + +* **docs-app:** remove stopWordFilter from lunr pipeline ([#4536](https://github.com/reactivex/rxjs/issues/4536)) ([9eaebd4](https://github.com/reactivex/rxjs/commit/9eaebd4)) +* **dtslint:** disable tests that break in TS@next ([#4705](https://github.com/reactivex/rxjs/issues/4705)) ([ecc73d2](https://github.com/reactivex/rxjs/commit/ecc73d2)) +* **index:** export NotificationKind ([#4514](https://github.com/reactivex/rxjs/issues/4514)) ([7125355](https://github.com/reactivex/rxjs/commit/7125355)), closes [#4513](https://github.com/reactivex/rxjs/issues/4513) +* **race:** better typings ([#4643](https://github.com/reactivex/rxjs/issues/4643)) ([fb9bc48](https://github.com/reactivex/rxjs/commit/fb9bc48)), closes [#4390](https://github.com/reactivex/rxjs/issues/4390) [#4642](https://github.com/reactivex/rxjs/issues/4642) +* **throwIfEmpty:** ensure result is retry-able ([c4f44b9](https://github.com/reactivex/rxjs/commit/c4f44b9)) +* **types:** Fixed signature for onErrorResumeNext ([#4603](https://github.com/reactivex/rxjs/issues/4603)) ([4dd0be0](https://github.com/reactivex/rxjs/commit/4dd0be0)) + + +### Features + +* **combineLatest:** deprecated rest argument and scheduler signatures ([#4641](https://github.com/reactivex/rxjs/issues/4641)) ([6661c79](https://github.com/reactivex/rxjs/commit/6661c79)), closes [#4640](https://github.com/reactivex/rxjs/issues/4640) +* **fetch:** We now export a `fromFetch` static observable creation method from `rxjs/fetch`. Mirrors native `fetch` only it's lazy and cancellable via `Observable` interface. ([#4702](https://github.com/reactivex/rxjs/issues/4702)) ([5a1ef86](https://github.com/reactivex/rxjs/commit/5a1ef86)) +* **forkJoin:** accepts a dictionary of sources ([#4640](https://github.com/reactivex/rxjs/issues/4640)) ([b5a2ac9](https://github.com/reactivex/rxjs/commit/b5a2ac9)) +* **partition:** new `partition` observable creation function. Old `partition` operator is deprecated ([#4419](https://github.com/reactivex/rxjs/issues/4419)) ([#4685](https://github.com/reactivex/rxjs/issues/4685)) ([d5d6980](https://github.com/reactivex/rxjs/commit/d5d6980)) +* **scheduled:** Add `scheduled` creation function to use to create scheduled observable of values. Deprecate scheduled versions of `from`, `range`, et al. ([#4595](https://github.com/reactivex/rxjs/issues/4595)) ([f57e1fc](https://github.com/reactivex/rxjs/commit/f57e1fc)) + + +### Performance Improvements + +* **Subscription:** improve parent management ([#4526](https://github.com/reactivex/rxjs/issues/4526)) ([06f1a25](https://github.com/reactivex/rxjs/commit/06f1a25)) + + + # [6.4.0](https://github.com/reactivex/rxjs/compare/6.3.3...6.4.0) (2019-01-30) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db71f452e5..57a12a794e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -129,7 +129,7 @@ from the main (upstream) repository: - The folder `docs-app` contains everything you need for building and developing the docs - the folder `doc` used to be the documentation, but should remain until all content is transferred. - The [Documentation README](docs_app/README.md) will support you -- After a PR is merged to master the docs will be published to https://rxjs-dev.firebaseapp.com/ +- After a PR is merged to master the docs will be published to https://rxjs.dev/ ## Unit Tests diff --git a/README.md b/README.md index df22c90434..20e4d8bcba 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Reactive Extensions Library for JavaScript. This is a rewrite of [Reactive-Exten - [Contribution Guidelines](CONTRIBUTING.md) - [Maintainer Guidelines](doc/maintainer-guidelines.md) - [Creating Operators](doc/operator-creation.md) -- [API Documentation (WIP)](https://rxjs-dev.firebaseapp.com/) +- [API Documentation (WIP)](https://rxjs.dev/) ## Versions In This Repository diff --git a/compat/package.json b/compat/package.json index b864866260..5c23213f6f 100644 --- a/compat/package.json +++ b/compat/package.json @@ -1,6 +1,6 @@ { "name": "rxjs-compat", - "version": "6.4.0", + "version": "6.5.0", "main": "./Rx.js", "license": "Apache-2.0", "typings": "./Rx.d.ts" diff --git a/doc/installation.md b/doc/installation.md index 0927324497..4a9f6511cf 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -6,7 +6,7 @@ npm install rxjs@beta Import just the parts you need and use them -```js +```ts import { of, fromEvent } from 'rxjs'; import { map, filter } from 'rxjs/operators'; @@ -29,7 +29,7 @@ npm install rxjs@beta Usage is pretty much the same thing, only with require: -```js +```ts const { of, fromEvent } = require('rxjs'); const { map, filter } = require('rxjs/operators'); @@ -48,7 +48,7 @@ fromEvent(input, 'input').pipe( You can use a CDN (shown below), if you like. In this case, everything is in the same location as it would be in the ESM or CJS versions, but they're namespaced like `rxjs` or `rxjs.operators` instead of `rxjs` and `rxjs/operators`. -```js +```ts const { of, fromEvent } = rxjs; const { map, filter } = rxjs.operators; diff --git a/doc/internal-marble-tests.md b/doc/internal-marble-tests.md index f55e4ac633..fa86bbe924 100644 --- a/doc/internal-marble-tests.md +++ b/doc/internal-marble-tests.md @@ -1,188 +1 @@ -> NOTE: This document refers to the writing marble tests for the **RxJS repo internals** and is intended for anyone wishing to help maintain the RxJS repo. **Users of RxJS** should instead view the [guide for writing marbles tests for applications](./marble-testing.md). The major difference is that the behavior of the TestScheduler differs between manual usage and using the `testScheduler.run(callback)` helper. - -# Writing Marble Tests - -"Marble Tests" are tests that use a specialized VirtualScheduler called the `TestScheduler`. They enable us to test -asynchronous operations in a synchronous and dependable manner. The "marble notation" is something that's been adapted -from many teachings and documents by people such as @jhusain, @headinthebox, @mattpodwysocki and @staltz. In fact, -@staltz first recommended this as a DSL for creating unit tests, and it has since been altered and adopted. - -#### links - -- [Contribution](../CONTRIBUTING.md) -- [Code of Conduct](../CODE_OF_CONDUCT.md) - -## Basic methods - -The unit tests have helper methods that have been added to make creating tests easier. - -- `hot(marbles: string, values?: object, error?: any)` - creates a "hot" observable (a subject) that will behave - as though it's already "running" when the test begins. An interesting difference is that `hot` marbles allow a - `^` character to signal where the "zero frame" is. That is the point at which the subscription to observables - being tested begins. -- `cold(marbles: string, values?: object, error?: any)` - creates a "cold" observable whose subscription starts when - the test begins. -- `expectObservable(actual: Observable).toBe(marbles: string, values?: object, error?: any)` - schedules an assertion - for when the TestScheduler flushes. The TestScheduler will automatically flush at the end of your jasmine `it` block. -- `expectSubscriptions(actualSubscriptionLogs: SubscriptionLog[]).toBe(subscriptionMarbles: string)` - like `expectObservable` schedules an assertion for when the testScheduler flushes. Both `cold()` and `hot()` return an observable with a property `subscriptions` of type `SubscriptionLog[]`. Give `subscriptions` as parameter to `expectSubscriptions` to assert whether it matches the `subscriptionsMarbles` marble diagram given in `toBe()`. Subscription marble diagrams are slightly different than Observable marble diagrams. Read more below. - -### Ergonomic defaults for `hot` and `cold` - -In both `hot` and `cold` methods, value characters specified in marble diagrams are emitted as strings unless a `values` -argument is passed to the method. Therefor: - -`hot('--a--b')` will emit `"a"` and `"b"` whereas - -`hot('--a--b', { a: 1, b: 2 })` will emit `1` and `2`. - -Likewise, unspecified errors will just default to the string `"error"`, so: - -`hot('---#')` will emit error `"error"` whereas - -`hot('---#', null, new SpecialError('test'))` will emit `new SpecialError('test')` - - -## Marble Syntax - -Marble syntax is a string which represents events happening over "time". The first character of any marble string - -always represents the "zero frame". A "frame" is somewhat analogous to a virtual millisecond. - -- `"-"` time: 10 "frames" of time passage. -- `"|"` complete: The successful completion of an observable. This is the observable producer signaling `complete()` -- `"#"` error: An error terminating the observable. This is the observable producer signaling `error()` -- `"a"` any character: All other characters represent a value being emitted by the producer signaling `next()` -- `"()"` sync groupings: When multiple events need to be in the same frame synchronously, parentheses are used - to group those events. You can group nexted values, a completion or an error in this manner. The position of the - initial `(` determines the time at which its values are emitted. -- `"^"` subscription point: (hot observables only) shows the point at which the tested observables will be subscribed - to the hot observable. This is the "zero frame" for that observable, every frame before the `^` will be negative. - -### Examples - -`'-'` or `'------'`: Equivalent to `Observable.never()`, or an observable that never emits or completes - -`|`: Equivalent to `Observable.empty()` - -`#`: Equivalent to `Observable.throw()` - -`'--a--'`: An observable that waits 20 "frames", emits value `a` and then never completes. - -`'--a--b--|'`: On frame 20 emit `a`, on frame 50 emit `b`, and on frame 80, `complete` - -`'--a--b--#'`: On frame 20 emit `a`, on frame 50 emit `b`, and on frame 80, `error` - -`'-a-^-b--|'`: In a hot observable, on frame -20 emit `a`, then on frame 20 emit `b`, and on frame 50, `complete`. - -`'--(abc)-|'`: on frame 20, emit `a`, `b`, and `c`, then on frame 80 `complete` - -`'-----(a|)'`: on frame 50, emit `a` and `complete`. - -## Subscription Marble Syntax - -The subscription marble syntax is slightly different to conventional marble syntax. It represents the **subscription** and an **unsubscription** points happening over time. There should be no other type of event represented in such diagram. - -- `"-"` time: 10 "frames" of the passage. -- `"^"` subscription point: shows the point in time at which a subscription happen. -- `"!"` unsubscription point: shows the point in time at which a subscription is unsubscribed. - -There should be **at most one** `^` point in a subscription marble diagram, and **at most one** `!` point. Other than that, the `-` character is the only one allowed in a subscription marble diagram. - -### Examples - -`'-'` or `'------'`: no subscription ever happened. - -`'--^--'`: a subscription happened after 20 "frames" of time passed, and the subscription was not unsubscribed. - -`'--^--!-'`: on frame 20 a subscription happened, and on frame 50 was unsubscribed. - -## Anatomy of a Test - -A basic test might look as follows: - -```js - -var e1 = hot('----a--^--b-------c--|'); -var e2 = hot( '---d-^--e---------f-----|'); -var expected = '---(be)----c-f-----|'; - -expectObservable(e1.merge(e2)).toBe(expected); -``` - -- The `^` characters of `hot` observables should **always** be aligned. -- The **first character** of `cold` observables or expected observables should **always** be aligned - with each other, and with the `^` of hot observables. -- Use default emission values when you can. Specify `values` when you have to. - -A test example with specified values: - -```js -var values = { - a: 1, - b: 2, - c: 3, - d: 4, - x: 1 + 3, // a + c - y: 2 + 4, // b + d -} -var e1 = hot('---a---b---|', values); -var e2 = hot('-----c---d---|', values); -var expected = '-----x---y---|'; - -expectObservable(e1.zip(e2, function(x, y) { return x + y; })) - .toBe(expected, values); -``` - -- Use the same hash to look up all values, this ensures that multiple uses of the same character have the - same value. -- Make the result values as obvious as possible as to what they represent, these are *tests* afterall, we want - clarity more than efficiency, so `x: 1 + 3, // a + c` is better than just `x: 4`. The former conveys *why* it's 4, - the latter does not. - -A test example with subscription assertions: - -```js -var x = cold( '--a---b---c--|'); -var xsubs = '------^-------!'; -var y = cold( '---d--e---f---|'); -var ysubs = '--------------^-------------!'; -var e1 = hot( '------x-------y------|', { x: x, y: y }); -var expected = '--------a---b----d--e---f---|'; - -expectObservable(e1.switch()).toBe(expected); -expectSubscriptions(x.subscriptions).toBe(xsubs); -expectSubscriptions(y.subscriptions).toBe(ysubs); -``` - -- Align the start of `xsubs` and `ysubs` diagrams with `expected` diagram. -- Notice how the `x` cold observable is unsubscribed at the same time `e1` emits `y`. - -In most tests it will be unnecessary to test subscription and unsubscription points, being either obvious or implied from the `expected` diagram. In those cases do not write subscription assertions. In test cases that have inner subscriptions or cold observables with multiple subscribers, these subscription assertions can be useful. - -## Generating PNG marble diagrams from tests - -Typically, each test case in Jasmine is written as `it('should do something', function () { /* ... */ })`. To mark a test case for PNG diagram generation, you must use the `asDiagram(label)` function, like this: - - -```js -it.asDiagram(operatorLabel)('should do something', function () { - // ... -}); -``` - -For instance, with `zip`, we would write - -```js -it.asDiagram('zip')('should zip by concatenating', function () { - var e1 = hot('---a---b---|'); - var e2 = hot('-----c---d---|'); - var expected = '-----x---y---|'; - var values = { x: 'ac', y: 'bd' }; - - var result = e1.zip(e2, function(x, y) { return String(x) + String(y); }); - - expectObservable(result).toBe(expected, values); -}); -``` - -Then, when running `npm run tests2png`, this test case will be parsed and a PNG file `zip.png` (filename determined by `${operatorLabel}.png`) will be created in the `img/` folder. +[Document moved here](../docs_app/content/guide/testing/internal-marble-tests.md) diff --git a/doc/introduction.md b/doc/introduction.md index e44e38150e..ee139d722b 100644 --- a/doc/introduction.md +++ b/doc/introduction.md @@ -19,7 +19,7 @@ The essential concepts in RxJS which solve async event management are: Normally you register event listeners. -```js +```ts const button = document.querySelector('button'); button.addEventListener('click', () => console.log('Clicked!')); @@ -27,7 +27,7 @@ button.addEventListener('click', () => console.log('Clicked!')); Using RxJS you create an observable instead. -```js +```ts import { fromEvent } from 'rxjs'; const button = document.querySelector('button'); @@ -42,7 +42,7 @@ What makes RxJS powerful is its ability to produce values using pure functions. Normally you would create an impure function, where other pieces of your code can mess up your state. -```js +```ts const button = document.querySelector('button'); let count = 0; @@ -72,7 +72,7 @@ RxJS has a whole range of operators that helps you control how the events flow t This is how you would allow at most one click per second, with plain JavaScript: -```js +```ts const button = document.querySelector('button'); const rate = 1000; let count = 0; @@ -88,7 +88,7 @@ button.addEventListener('click', () => { With RxJS: -```js +```ts import { fromEvent } from 'rxjs'; import { throttleTime, scan } from 'rxjs/operators'; @@ -110,7 +110,7 @@ You can transform the values passed through your observables. Here's how you can add the current mouse x position for every click, in plain JavaScript: -```js +```ts const button = document.querySelector('button'); const rate = 1000; let count = 0; @@ -127,7 +127,7 @@ button.addEventListener('click', (event) => { With RxJS: -```js +```ts import { fromEvent } from 'rxjs'; import { throttleTime, map, scan } from 'rxjs/operators'; diff --git a/doc/marble-testing.md b/doc/marble-testing.md index bd5f54d43b..0458e60a4e 100644 --- a/doc/marble-testing.md +++ b/doc/marble-testing.md @@ -1,181 +1 @@ -> IMPORTANT: This guide refers to usage of marble diagrams when using the new `testScheduler.run(callback)`. Some details here do not apply to using the TestScheduler manually, without using the `run()` helper. - -# Testing RxJS Code with Marble Diagrams - -We can test our _asynchronous_ RxJS code _synchronously_ and deterministically by virtualizing time using the TestScheduler. ASCII **marble diagrams** provide a visual way for us to represent the behavior of an Observable. We can use them to assert that a particular Observable behaves as expected, as well as to create [hot and cold Observables](https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339) we can use as mocks. - -> At this time the TestScheduler can only be used to test code that uses timers, like delay/debounceTime/etc (i.e. it uses AsyncScheduler with delays > 1). If the code consumes a Promise or does scheduling with AsapScheduler/AnimationFrameScheduler/etc it cannot be reliably tested with TestScheduler, but instead should be tested more traditionally. See the [Known Issues](#known-issues) section for more details. - -```ts -import { TestScheduler } from 'rxjs/testing'; - -const scheduler = new TestScheduler((actual, expected) => { - // asserting the two objects are equal - // e.g. using chai. - expect(actual).deep.equal(expected); -}); - -// This test will actually run *synchronously* -it('generate the stream correctly', () => { - scheduler.run(helpers => { - const { cold, expectObservable, expectSubscriptions } = helpers; - const e1 = cold('-a--b--c---|'); - const subs = '^----------!'; - const expected = '-a-----c---|'; - - expectObservable(e1.pipe(throttleTime(3, scheduler))).toBe(expected); - expectSubscriptions(e1.subscriptions).toBe(subs); - }); -}); -``` - -## API - -The callback function you provide to `testScheduler.run(callback)` is called with `helpers` object that contains functions you'll use to write your tests. - -> When the code inside this callback is being executed, any operator that uses timers/AsyncScheduler (like delay, debounceTime, etc) will **automatically** use the TestScheduler instead, so that we have "virtual time". You do not need to pass the TestScheduler to them, like in the past. - -```ts -testScheduler.run(helpers => { - const { cold, hot, expectObservable, expectSubscriptions, flush } = helpers; - // use them -}); -``` - -- `hot(marbleDiagram: string, values?: object, error?: any)` - creates a ["hot" observable](https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339) (like a subject) that will behave as though it's already "running" when the test begins. An interesting difference is that `hot` marbles allow a `^` character to signal where the "zero frame" is. That is the point at which the subscription to observables being tested begins. -- `cold(marbleDiagram: string, values?: object, error?: any)` - creates a ["cold" observable](https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339) whose subscription starts when the test begins. -- `expectObservable(actual: Observable).toBe(marbleDiagram: string, values?: object, error?: any)` - schedules an assertion for when the TestScheduler flushes. Give `subscriptionMarbles` as parameter to change the schedule of subscription and unsubscription. If you don't provide the `subscriptionMarbles` parameter it will subscribe at the beginning and never unsubscribe. Read below about subscription marble diagram. -- `expectSubscriptions(actualSubscriptionLogs: SubscriptionLog[]).toBe(subscriptionMarbles: string)` - like `expectObservable` schedules an assertion for when the testScheduler flushes. Both `cold()` and `hot()` return an observable with a property `subscriptions` of type `SubscriptionLog[]`. Give `subscriptions` as parameter to `expectSubscriptions` to assert whether it matches the `subscriptionsMarbles` marble diagram given in `toBe()`. Subscription marble diagrams are slightly different than Observable marble diagrams. Read more below. -- `flush()` - immediately starts virtual time. Not often used since `run()` will automatically flush for you when your callback returns, but in some cases you may wish to flush more than once or otherwise have more control. - -## Marble Syntax - -In the context of TestScheduler, a marble diagram is a string containing special syntax representing events happening over virtual time. Time progresses by _frames_. The first character of any marble string always represents the _zero frame_, or the start of time. Inside of `testScheduler.run(callback)` the frameTimeFactor is set to 1, which means one frame is equal to one virtual millisecond. - -How many virtual milliseconds one frame represents depends on the value of `TestScheduler.frameTimeFactor`. For legacy reasons the value of `frameTimeFactor` is 1 _only_ when your code inside the `testScheduler.run(callback)` callback is running. Outside of it, it's set to 10. This will likely change in a future version of RxJS so that it is always 1. - -> IMPORTANT: This syntax guide refers to usage of marble diagrams when using the new `testScheduler.run(callback)`. The semantics of marble diagrams when using the TestScheduler manually are different, and some features like the new time progression syntax are not supported. - -- `' '` whitespace: horizontal whitespace is ignored, and can be used to help vertically align multiple marble diagrams. -- `'-'` frame: 1 "frame" of virtual time passing (see above description of frames). -- `[0-9]+[ms|s|m]` time progression: the time progression syntax lets you progress virtual time by a specific amount. It's a number, followed by a time unit of `ms` (milliseconds), `s` (seconds), or `m` (minutes) without any space between them, e.g. `a 10ms b`. See [Time progression syntax](#Time-progression-syntax) for more details. -- `'|'` complete: The successful completion of an observable. This is the observable producer signaling `complete()`. -- `'#'` error: An error terminating the observable. This is the observable producer signaling `error()`. -- `[a-z0-9]` e.g. `'a'` any alphanumeric character: Represents a value being emitted by the producer signaling `next()`. -- `'()'` sync groupings: When multiple events need to be in the same frame synchronously, parentheses are used to group those events. You can group next'd values, a completion, or an error in this manner. The position of the initial `(` determines the time at which its values are emitted. While it can be unintuitive at first, after all the values have synchronously emitted time will progress a number of frames equal to the number of ASCII characters in the group, including the parentheses. e.g. `'(abc)'` will emit the values of a, b, and c synchronously in the same frame and then advance virtual time by 5 frames, `'(abc)'.length === 5`. This is done because it often helps you vertically align your marble diagrams, but it's a known pain point in real-world testing. [Learn more about known issues](#known-issues). -- `'^'` subscription point: (hot observables only) shows the point at which the tested observables will be subscribed to the hot observable. This is the "zero frame" for that observable, every frame before the `^` will be negative. Negative time might seem pointless, but there are in fact advanced cases where this is necessary, usually involving ReplaySubjects. - -### Time progression syntax - -The new time progression syntax takes inspiration from the CSS duration syntax. It's a number (int or float) immediately followed by a unit; ms (milliseconds), s (seconds), m (minutes). e.g. `100ms`, `1.4s`, `5.25m`. - -When it's not the first character of the diagram it must be padded a space before/after to disambiguate it from a series of marbles. e.g. `a 1ms b` needs the spaces because `a1msb` will be interpreted as `['a', '1', 'm', 's', 'b']` where each of these characters is a value that will be next()'d as-is. - -**NOTE**: You may have to subtract 1 millisecond from the time you want to progress because the alphanumeric marbles (representing an actual emitted value) _advance time 1 virtual frame_ themselves already, after they emit. This can be very unintuitive and frustrating, but for now it is indeed correct. - -```ts -const input = ' -a-b-c|'; -const expected = '-- 9ms a 9ms b 9ms (c|)'; -/* - -// Depending on your personal preferences you could also -// use frame dashes to keep vertical aligment with the input -const input = ' -a-b-c|'; -const expected = '------- 4ms a 9ms b 9ms (c|)'; -// or -const expected = '-----------a 9ms b 9ms (c|)'; - -*/ - -const result = cold(input).pipe( - concatMap(d => of(d).pipe( - delay(10) - )) -); - -expectObservable(result).toBe(expected); -``` - -### Examples - -`'-'` or `'------'`: Equivalent to `never()`, or an observable that never emits or completes - -`|`: Equivalent to `empty()` - -`#`: Equivalent to `throwError()` - -`'--a--'`: An observable that waits 2 "frames", emits value `a` and then never completes. - -`'--a--b--|'`: On frame 2 emit `a`, on frame 5 emit `b`, and on frame 8, `complete` - -`'--a--b--#'`: On frame 2 emit `a`, on frame 5 emit `b`, and on frame 8, `error` - -`'-a-^-b--|'`: In a hot observable, on frame -2 emit `a`, then on frame 2 emit `b`, and on frame 5, `complete`. - -`'--(abc)-|'`: on frame 2 emit `a`, `b`, and `c`, then on frame 8 `complete` - -`'-----(a|)'`: on frame 5 emit `a` and `complete`. - -`'a 9ms b 9s c|'`: on frame 0 emit `a`, on frame 10 emit `b`, on frame 10,012 emit `c`, then on on frame 10,013 `complete`. - -`'--a 2.5m b'`: on frame 2 emit `a`, on frame 150,003 emit `b` and never complete. - -## Subscription Marbles - -The `expectSubscriptions` helper allows you to assert that a `cold()` or `hot()` Observable you created was subscribed/unsubscribed to at the correct point in time. The subscription marble syntax is slightly different to conventional marble syntax. - -- `'-'` time: 1 frame time passing. -- `[0-9]+[ms|s|m]` time progression: the time progression syntax lets you progress virtual time by a specific amount. It's a number, followed by a time unit of `ms` (milliseconds), `s` (seconds), or `m` (minutes) without any space between them, e.g. `a 10ms b`. See [Time progression syntax](#Time-progression-syntax) for more details. -- `'^'` subscription point: shows the point in time at which a subscription happen. -- `'!'` unsubscription point: shows the point in time at which a subscription is unsubscribed. - -There should be **at most one** `^` point in a subscription marble diagram, and **at most one** `!` point. Other than that, the `-` character is the only one allowed in a subscription marble diagram. - -### Examples - -`'-'` or `'------'`: no subscription ever happened. - -`'--^--'`: a subscription happened after 2 "frames" of time passed, and the subscription was not unsubscribed. - -`'--^--!-'`: on frame 2 a subscription happened, and on frame 5 was unsubscribed. - -`'500ms ^ 1s !'`: on frame 500 a subscription happened, and on frame 1,501 was unsubscribed. - -*** - -## Known Issues - -#### You can't directly test RxJS code that consumes Promises or uses any of the other schedulers (e.g. AsapScheduler) - -If you have RxJS code that uses any other form of async scheduling other than AsyncScheduler, e.g. Promises, AsapScheduler, etc. you can't reliably use marble diagrams _for that particular code_. This is because those other scheduling methods won't be virtualized or known to TestScheduler. - -The solution is to test that code in isolation, with the traditional async testing methods of your testing framework. The specifics depend on your testing framework of choice, but here's a pseudo-code example: - -```ts -// Some RxJS code that also consumes a Promise, so TestScheduler won't be able -// to correctly virtualize and the test will always be really async -const myAsyncCode = () => from(Promise.resolve('something')); - -it('has async code', done => { - myAsyncCode().subscribe(d => { - assertEqual(d, 'something'); - done(); - }); -}); -``` - -On a related note, you also can't currently assert delays of zero, even with AsyncScheduler, e.g. `delay(0)` is like saying `setTimeout(work, 0)`. This schedules a new ["task" aka "macrotask"](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/), so it's async, but without an explicit passage of time. - -#### Behavior is different outside of `testScheduler.run(callback)` - -The TestScheduler has been around since v5, but was actually intended for testing RxJS itself by the maintainers, rather than for use in regular user apps. Because of this, some of the default behaviors and features of the TestScheduler didn't work well (or at all) for users. In v6 we introduced the `testScheduler.run(callback)` method which allowed us to provide new defaults and features in a non-breaking way, but it's still possible to [use the TestScheduler outside](./internal-marble-tests.md) of `testScheduler.run(callback)`. It's important to note that if you do so, there are some major differences in how it will behave. - -* TestScheduler helper methods have more verbose names, like `testScheduler.createColdObservable()` instead of `cold()` -* The testScheduler instance is NOT automatically be used by operators that uses AsyncScheduler, e.g. delay, debounceTime, etc so you have to explicitly pass it to them. -* There is NO support for time progression syntax e.g. `-a 100ms b-|` -* 1 frame is 10 virtual milliseconds by default. i.e. `TestScheduler.frameTimeFactor = 10` -* Each space ` ` equals 1 frame, same as a hyphen `-`. -* There is a hard maximum number of frames set at 750 i.e. `maxFrames = 750`. After 750 they are silently ignored. -* You must explicitly flush the scheduler - -While at this time usage of the TestScheduler outside of `testScheduler.run(callback)` has not been officially deprecated, it is discouraged because it is likely to cause confusion. +[Document moved here](../docs_app/content/guide/testing/marble-testing.md) diff --git a/doc/observable.md b/doc/observable.md index 4cc0d1233e..05259e7e0d 100644 --- a/doc/observable.md +++ b/doc/observable.md @@ -148,7 +148,7 @@ This happens because both functions and Observables are lazy computations. If yo Some people claim that Observables are asynchronous. That is not true. If you surround a function call with logs, like this: -```js +```ts console.log('before'); console.log(foo.call()); console.log('after'); @@ -165,7 +165,7 @@ You will see the output: And this is the same behavior with Observables: -```js +```ts console.log('before'); foo.subscribe(x => { console.log(x); @@ -188,7 +188,7 @@ Which proves the subscription of `foo` was entirely synchronous, just like a fun What is the difference between an Observable and a function? **Observables can "return" multiple values over time**, something which functions cannot. You can't do this: -```js +```ts function foo() { console.log('Hello'); return 42; @@ -404,7 +404,7 @@ Each Observable must define how to dispose resources of that execution when we c For instance, this is how we clear an interval execution set with `setInterval`: -```js +```ts const observable = new Observable(function subscribe(subscriber) { // Keep track of the interval resource const intervalId = setInterval(() => { @@ -420,7 +420,7 @@ const observable = new Observable(function subscribe(subscriber) { Just like `observable.subscribe` resembles `new Observable(function subscribe() {...})`, the `unsubscribe` we return from `subscribe` is conceptually equal to `subscription.unsubscribe`. In fact, if we remove the ReactiveX types surrounding these concepts, we're left with rather straightforward JavaScript. -```js +```ts function subscribe(subscriber) { const intervalId = setInterval(() => { subscriber.next('hi'); diff --git a/doc/observer.md b/doc/observer.md index 3bd6776c0c..b02d74269a 100644 --- a/doc/observer.md +++ b/doc/observer.md @@ -2,8 +2,8 @@ **What is an Observer?** An Observer is a consumer of values delivered by an Observable. Observers are simply a set of callbacks, one for each type of notification delivered by the Observable: `next`, `error`, and `complete`. The following is an example of a typical Observer object: -```js -var observer = { +```ts +const observer = { next: x => console.log('Observer got a next value: ' + x), error: err => console.error('Observer got an error: ' + err), complete: () => console.log('Observer got a complete notification'), @@ -13,7 +13,7 @@ var observer = { To use the Observer, provide it to the `subscribe` of an Observable: -```js +```ts observable.subscribe(observer); ``` @@ -23,8 +23,8 @@ Observers in RxJS may also be *partial*. If you don't provide one of the callbac The example below is an Observer without the `complete` callback: -```js -var observer = { +```ts +const observer = { next: x => console.log('Observer got a next value: ' + x), error: err => console.error('Observer got an error: ' + err), }; @@ -33,14 +33,14 @@ var observer = { When subscribing to an Observable, you may also just provide the callbacks as arguments, without being attached to an Observer object, for instance like this: -```js +```ts observable.subscribe(x => console.log('Observer got a next value: ' + x)); ``` Internally in `observable.subscribe`, it will create an Observer object using the first callback argument as the `next` handler. All three types of callbacks may be provided as arguments: -```js +```ts observable.subscribe( x => console.log('Observer got a next value: ' + x), err => console.error('Observer got an error: ' + err), diff --git a/doc/operator-creation.md b/doc/operator-creation.md index 5bf41d50b7..009b9c2a9f 100644 --- a/doc/operator-creation.md +++ b/doc/operator-creation.md @@ -30,15 +30,15 @@ any way the developer sees fit, but here are some guidelines: ### Example -```js +```ts function mySimpleOperator(someCallback) { - // We *could* do a `var self = this;` here to close over, but see next comment + // We *could* do a `const self = this;` here to close over, but see next comment return Observable.create(subscriber => { // because we're in an arrow function `this` is from the outer scope. - var source = this; + const source = this; // save our inner subscription - var subscription = source.subscribe(value => { + const subscription = source.subscribe(value => { // important: catch errors from user-provided callbacks try { subscriber.next(someCallback(value)); @@ -64,14 +64,14 @@ There are a few ways to do this. It's really down to needs and preference: 1) Use the ES7 function bind operator (`::`) available in transpilers like [BabelJS](http://babeljs.io): -```js +```ts someObservable::mySimpleOperator(x => x + '!'); ``` 2) Create your own Observable subclass and override `lift` to return it: -```js +```ts class MyObservable extends Observable { lift(operator) { const observable = new MyObservable(); //<-- important part here @@ -93,7 +93,7 @@ MyObservable.prototype.mySimpleOperator = mySimpleOperator; 3) Patch `Observable.prototype` directly: -```js +```ts Observable.prototype.mySimpleOperator = mySimpleOperator; // ... and later .../ @@ -108,12 +108,12 @@ If you don't want to patch the Observable prototype, you can also write the oper Example implementation: -```js +```ts function mySimpleOperator(someCallback) { // notice that we return a function here return function mySimpleOperatorImplementation(source) { return Observable.create(subscriber => { - var subscription = source.subscribe(value => { + const subscription = source.subscribe(value => { try { subscriber.next(someCallback(value)); } catch(err) { @@ -132,7 +132,7 @@ function mySimpleOperator(someCallback) { This can now be used with the `pipe()` method on the Observable: -```js +```ts const obs = someObservable.pipe(mySimpleOperator(x => x + '!')); ``` diff --git a/doc/operators.md b/doc/operators.md index b36e45aa8d..1953c48bf3 100644 --- a/doc/operators.md +++ b/doc/operators.md @@ -10,9 +10,11 @@ Operators are **methods** on the Observable type, such as `.map(...)`, `.filter( An Operator is essentially a pure function which takes one Observable as input and generates another Observable as output. Subscribing to the output Observable will also subscribe to the input Observable. In the following example, we create a custom operator function that multiplies each value received from the input Observable by 10: -```js +```ts +import {Observable, from} from 'rxjs'; + function multiplyByTen(input) { - var output = Rx.Observable.create(function subscribe(observer) { + const output = Observable.create(function subscribe(observer) { input.subscribe({ next: (v) => observer.next(10 * v), error: (err) => observer.error(err), @@ -22,8 +24,8 @@ function multiplyByTen(input) { return output; } -var input = Rx.Observable.from([1, 2, 3, 4]); -var output = multiplyByTen(input); +const input = from([1, 2, 3, 4]); +const output = multiplyByTen(input); output.subscribe(x => console.log(x)); ``` @@ -42,10 +44,12 @@ Notice that a subscribe to `output` will cause `input` Observable to be subscrib **What is an instance operator?** Typically when referring to operators, we assume *instance* operators, which are methods on Observable instances. For instance, if the operator `multiplyByTen` would be an official instance operator, it would look roughly like this: -```js -Rx.Observable.prototype.multiplyByTen = function multiplyByTen() { - var input = this; - return Rx.Observable.create(function subscribe(observer) { +```ts +import {Observable} from 'rxjs'; + +Observable.prototype.multiplyByTen = function multiplyByTen() { + const input = this; + return Observable.create(function subscribe(observer) { input.subscribe({ next: (v) => observer.next(10 * v), error: (err) => observer.error(err), @@ -59,8 +63,9 @@ Rx.Observable.prototype.multiplyByTen = function multiplyByTen() { Notice how the `input` Observable is not a function argument anymore, it is assumed to be the `this` object. This is how we would use such instance operator: -```js -var observable = Rx.Observable.from([1, 2, 3, 4]).multiplyByTen(); +```ts +import {Observable, from} from 'rxjs'; +const observable = from([1, 2, 3, 4]).multiplyByTen(); observable.subscribe(x => console.log(x)); ``` @@ -73,19 +78,22 @@ The most common type of static operators are the so-called *Creation Operators*. A typical example of a static creation operator would be the `interval` function. It takes a number (not an Observable) as input argument, and produces an Observable as output: -```js -var observable = Rx.Observable.interval(1000 /* number of milliseconds */); +```ts +import {interval} from 'rxjs'; + +const observable = interval(1000 /* number of milliseconds */); ``` Another example of a creation operator is `create`, which we have been using extensively in previous examples. See the list of [all static creation operators here](#creation-operators). However, static operators may be of different nature than simply creation. Some *Combination Operators* may be static, such as `merge`, `combineLatest`, `concat`, etc. These make sense as static operators because they take *multiple* Observables as input, not just one, for instance: -```js -var observable1 = Rx.Observable.interval(1000); -var observable2 = Rx.Observable.interval(400); +```ts +import {interval, merge} from 'rxjs'; +const observable1 = interval(1000); +const observable2 = interval(400); -var merged = Rx.Observable.merge(observable1, observable2); +const merged = merge(observable1, observable2); ``` ## Marble diagrams diff --git a/doc/pipeable-operators.md b/doc/pipeable-operators.md index 647abe658c..c4d1800fe7 100644 --- a/doc/pipeable-operators.md +++ b/doc/pipeable-operators.md @@ -170,7 +170,7 @@ module.exports = { More complete configuration (closer to a real-world scenario): -```js +```ts const webpack = require('webpack'); const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); @@ -179,7 +179,7 @@ const nodeEnv = process.env.NODE_ENV || 'development'; const isProd = nodeEnv === 'production'; const rxPaths = require('rxjs/_esm5/path-mapping'); -var config = { +const config = { devtool: isProd ? 'hidden-source-map' : 'cheap-eval-source-map', context: path.resolve('./src'), entry: { diff --git a/doc/subject.md b/doc/subject.md index 7aa6f0a9b1..2aeebaea7b 100644 --- a/doc/subject.md +++ b/doc/subject.md @@ -315,7 +315,7 @@ setTimeout(() => { The AsyncSubject is a variant where only the last value of the Observable execution is sent to its observers, and only when the execution completes. -```js +```ts import { AsyncSubject } from 'rxjs'; const subject = new AsyncSubject(); diff --git a/doc/tutorial/applications.md b/doc/tutorial/applications.md index 6e52b0bbc5..24b45f2b7f 100644 --- a/doc/tutorial/applications.md +++ b/doc/tutorial/applications.md @@ -3,9 +3,11 @@ RxJS is a great tool to keep your code less error prone. It does that by using pure and stateless functions. But applications are stateful, so how do we bridge the stateless world of RxJS with the stateful world of our applications? Let us create a simple state store of the value `0`. On each click we want to increase that count in our state store. -```js -var button = document.querySelector('button'); -Rx.Observable.fromEvent(button, 'click').pipe( +```ts +import { fromEvent } from 'rxjs'; + +const button = document.querySelector('button'); +fromEvent(button, 'click').pipe( // scan (reduce) to a stream of counts scan(count => count + 1, 0) // Set the count on an element each time it changes @@ -16,9 +18,11 @@ So producing state is within the world of RxJS, but changing the DOM is a side e ## State stores Applications use state stores to hold state. These are called different things in different frameworks, like store, reducer and model, but at the core they are all just a plain object. What we also need to handle is that multiple observables can update a single state store. -```js -var increaseButton = document.querySelector('#increase'); -var increase = Rx.Observable.fromEvent(increaseButton, 'click').pipe( +```ts +import { fromEvent } from 'rxjs'; + +const increaseButton = document.querySelector('#increase'); +const increase = fromEvent(increaseButton, 'click').pipe( // We map to a function that will change our state map(() => state => Object.assign({}, state, {count: state.count + 1})) ); @@ -26,43 +30,47 @@ var increase = Rx.Observable.fromEvent(increaseButton, 'click').pipe( What we do here is mapping a click event to a state changing function. So instead of mapping to a value, we map to a function. A function will change the state of our state store. So now let us see how we actually make the change. -```js -var increaseButton = document.querySelector('#increase'); -var increase = Rx.Observable.fromEvent(increaseButton, 'click').pipe( +```ts +import { fromEvent } from 'rxjs'; + +const increaseButton = document.querySelector('#increase'); +const increase = fromEvent(increaseButton, 'click').pipe( map(() => state => Object.assign({}, state, {count: state.count + 1})) ); // We create an object with our initial state. Whenever a new state change function // is received we call it and pass the state. The new state is returned and // ready to be changed again on the next click -var state = increase.pipe( +const state = increase.pipe( scan((state, changeFn) => changeFn(state), {count: 0}) ); ``` We can now add a couple of more observables which will also change the same state store. -```js -var increaseButton = document.querySelector('#increase'); -var increase = Rx.Observable.fromEvent(increaseButton, 'click').pipe( +```ts +import { fromEvent, merge } from 'rxjs'; + +const increaseButton = document.querySelector('#increase'); +const increase = fromEvent(increaseButton, 'click').pipe( // Again we map to a function the will increase the count map(() => state => Object.assign({}, state, {count: state.count + 1})) ); -var decreaseButton = document.querySelector('#decrease'); -var decrease = Rx.Observable.fromEvent(decreaseButton, 'click').pipe( +const decreaseButton = document.querySelector('#decrease'); +const decrease = fromEvent(decreaseButton, 'click').pipe( // We also map to a function that will decrease the count map(() => state => Object.assign({}, state, {count: state.count - 1})) ); -var inputElement = document.querySelector('#input'); -var input = Rx.Observable.fromEvent(inputElement, 'keypress').pipe( +const inputElement = document.querySelector('#input'); +const input = fromEvent(inputElement, 'keypress').pipe( // Let us also map the keypress events to produce an inputValue state map(event => state => Object.assign({}, state, {inputValue: event.target.value})) ); // We merge the three state change producing observables -var state = Rx.Observable.merge( +const state = merge( increase, decrease, input @@ -81,7 +89,7 @@ state.subscribe((state) => { // To optimize our rendering we can check what state // has actually changed -var prevState = {}; +const prevState = {}; state.subscribe((state) => { if (state.count !== prevState.count) { document.querySelector('#count').innerHTML = state.count; @@ -99,16 +107,16 @@ We can take the state store approach and use it with many different frameworks a You can also create a global state store for your application using [Immutable JS](https://facebook.github.io/immutable-js/). Immutable JS is a great way to create immutable state stores that allows you to optimize rendering by doing shallow checks on changed values. -```js +```ts import Immutable from 'immutable'; import someObservable from './someObservable'; import someOtherObservable from './someOtherObservable'; -var initialState = { +const initialState = { foo: 'bar' }; -var state = Observable.merge( +const state = Observable.merge( someObservable, someOtherObservable ).pipe( @@ -121,7 +129,7 @@ export default state; Now you can import your state in whatever UI layer you are using. -```js +```ts import state from './state'; state.subscribe(state => { @@ -133,7 +141,7 @@ state.subscribe(state => { Lets look at an example where we subscribe to an observable when the component mounts and unsubscribes when it unmounts. -```js +```ts import messages from './someObservable'; class MyComponent extends ObservableComponent { diff --git a/doc/tutorial/basics.md b/doc/tutorial/basics.md index bcc136a614..10ef5f87e8 100644 --- a/doc/tutorial/basics.md +++ b/doc/tutorial/basics.md @@ -2,41 +2,47 @@ ## Converting to observables -```js +```ts +import { of, from, fromEvent, fromPromise, bindNodeCallback } from 'rxjs'; + // From one or multiple values -Rx.Observable.of('foo', 'bar'); +of('foo', 'bar'); // From array of values -Rx.Observable.from([1,2,3]); +from([1,2,3]); // From an event -Rx.Observable.fromEvent(document.querySelector('button'), 'click'); +fromEvent(document.querySelector('button'), 'click'); // From a Promise -Rx.Observable.fromPromise(fetch('/users')); +fromPromise(fetch('/users')); // From a callback (last argument is a callback) // fs.exists = (path, cb(exists)) -var exists = Rx.Observable.bindCallback(fs.exists); +const exists = bindCallback(fs.exists); exists('file.txt').subscribe(exists => console.log('Does file exist?', exists)); // From a callback (last argument is a callback) // fs.rename = (pathA, pathB, cb(err, result)) -var rename = Rx.Observable.bindNodeCallback(fs.rename); +const rename = bindNodeCallback(fs.rename); rename('file.txt', 'else.txt').subscribe(() => console.log('Renamed!')); ``` ## Creating observables Externally produce new events. -```js -var myObservable = new Rx.Subject(); +```ts +import { Subject } from 'rxjs'; + +const myObservable = new Subject(); myObservable.subscribe(value => console.log(value)); myObservable.next('foo'); ``` Internally produce new events. -```js -var myObservable = Rx.Observable.create(observer => { +```ts +import { Observable } from 'rxjs'; + +const myObservable = Observable.create(observer => { observer.next('foo'); setTimeout(() => observer.next('bar'), 1000); }); @@ -46,9 +52,11 @@ myObservable.subscribe(value => console.log(value)); Which one you choose depends on the scenario. The normal **Observable** is great when you want to wrap functionality that produces values over time. An example would be a websocket connection. With **Subject** you can trigger new events from anywhere really and you can connect existing observables to it. ## Controlling the flow -```js +```ts +import { fromEvent } from 'rxjs'; + // typing "hello world" -var input = Rx.Observable.fromEvent(document.querySelector('input'), 'input'); +const input = fromEvent(document.querySelector('input'), 'input'); // Filter out target values less than 3 characters long input.pipe( @@ -86,7 +94,7 @@ input.pipe( .subscribe(value => console.log(value)); // "hel" // Passes through events until other observable triggers an event -var stopStream = Rx.Observable.fromEvent(document.querySelector('button'), 'click'); +const stopStream = fromEvent(document.querySelector('button'), 'click'); input.pipe( takeUntil(stopStream), map(event => event.target.value) @@ -95,9 +103,11 @@ input.pipe( ``` ## Producing values -```js +```ts +import { fromEvent } from 'rxjs'; + // typing "hello world" -var input = Rx.Observable.fromEvent(document.querySelector('input'), 'input'); +const input = fromEvent(document.querySelector('input'), 'input'); // Pass on a new value input.pipe( diff --git a/doc/writing-marble-tests.md b/doc/writing-marble-tests.md index 3bf9b9a67b..692b8da734 100644 --- a/doc/writing-marble-tests.md +++ b/doc/writing-marble-tests.md @@ -1,2 +1,2 @@ -- [USERS: Writing marble diagram tests for your application](./marble-testing.md). -- [INTERNAL MAINTAINERS ONLY: Writing marble diagram tests for the RxJS internals](./internal-marble-tests.md). +- [USERS: Writing marble diagram tests for your application](../docs_app/content/guide/testing/marble-testing.md). +- [INTERNAL MAINTAINERS ONLY: Writing marble diagram tests for the RxJS internals](../docs_app/content/guide/testing/internal-marble-tests.md). diff --git a/docs_app/assets/images/marble-diagrams/NEVER.png b/docs_app/assets/images/marble-diagrams/NEVER.png new file mode 100644 index 0000000000..d6c8b526d0 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/NEVER.png differ diff --git a/docs_app/assets/images/marble-diagrams/audit.png b/docs_app/assets/images/marble-diagrams/audit.png new file mode 100644 index 0000000000..c0e2e01419 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/audit.png differ diff --git a/docs_app/assets/images/marble-diagrams/auditTime.png b/docs_app/assets/images/marble-diagrams/auditTime.png new file mode 100644 index 0000000000..ad78ffccba Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/auditTime.png differ diff --git a/docs_app/assets/images/marble-diagrams/buffer.png b/docs_app/assets/images/marble-diagrams/buffer.png new file mode 100644 index 0000000000..0109fc9cd3 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/buffer.png differ diff --git a/docs_app/assets/images/marble-diagrams/bufferCount.png b/docs_app/assets/images/marble-diagrams/bufferCount.png new file mode 100644 index 0000000000..b29274e629 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/bufferCount.png differ diff --git a/docs_app/assets/images/marble-diagrams/bufferTime.png b/docs_app/assets/images/marble-diagrams/bufferTime.png new file mode 100644 index 0000000000..62a6a22ab9 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/bufferTime.png differ diff --git a/docs_app/assets/images/marble-diagrams/bufferToggle.png b/docs_app/assets/images/marble-diagrams/bufferToggle.png new file mode 100644 index 0000000000..f355da97c7 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/bufferToggle.png differ diff --git a/docs_app/assets/images/marble-diagrams/bufferWhen.png b/docs_app/assets/images/marble-diagrams/bufferWhen.png new file mode 100644 index 0000000000..4c618ec356 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/bufferWhen.png differ diff --git a/docs_app/assets/images/marble-diagrams/catch.png b/docs_app/assets/images/marble-diagrams/catch.png new file mode 100644 index 0000000000..e08b6c0c9c Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/catch.png differ diff --git a/docs_app/assets/images/marble-diagrams/combineAll.png b/docs_app/assets/images/marble-diagrams/combineAll.png new file mode 100644 index 0000000000..211af1e256 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/combineAll.png differ diff --git a/docs_app/assets/images/marble-diagrams/combineLatest.png b/docs_app/assets/images/marble-diagrams/combineLatest.png new file mode 100644 index 0000000000..67ebd5a011 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/combineLatest.png differ diff --git a/docs_app/assets/images/marble-diagrams/concat.png b/docs_app/assets/images/marble-diagrams/concat.png new file mode 100644 index 0000000000..8dd83696b1 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/concat.png differ diff --git a/docs_app/assets/images/marble-diagrams/concatAll.png b/docs_app/assets/images/marble-diagrams/concatAll.png new file mode 100644 index 0000000000..968d47e09b Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/concatAll.png differ diff --git a/docs_app/assets/images/marble-diagrams/concatMap.png b/docs_app/assets/images/marble-diagrams/concatMap.png new file mode 100644 index 0000000000..137cf4def6 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/concatMap.png differ diff --git a/docs_app/assets/images/marble-diagrams/concatMapTo.png b/docs_app/assets/images/marble-diagrams/concatMapTo.png new file mode 100644 index 0000000000..226878800a Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/concatMapTo.png differ diff --git a/docs_app/assets/images/marble-diagrams/count.png b/docs_app/assets/images/marble-diagrams/count.png new file mode 100644 index 0000000000..ae9b81071e Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/count.png differ diff --git a/docs_app/assets/images/marble-diagrams/create.png b/docs_app/assets/images/marble-diagrams/create.png new file mode 100644 index 0000000000..f329eaa6e8 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/create.png differ diff --git a/docs_app/assets/images/marble-diagrams/debounce.png b/docs_app/assets/images/marble-diagrams/debounce.png new file mode 100644 index 0000000000..567554dad9 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/debounce.png differ diff --git a/docs_app/assets/images/marble-diagrams/debounceTime.png b/docs_app/assets/images/marble-diagrams/debounceTime.png new file mode 100644 index 0000000000..864328a073 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/debounceTime.png differ diff --git a/docs_app/assets/images/marble-diagrams/defaultIfEmpty.png b/docs_app/assets/images/marble-diagrams/defaultIfEmpty.png new file mode 100644 index 0000000000..d56f1ff4f8 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/defaultIfEmpty.png differ diff --git a/docs_app/assets/images/marble-diagrams/defer.png b/docs_app/assets/images/marble-diagrams/defer.png new file mode 100644 index 0000000000..6853738137 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/defer.png differ diff --git a/docs_app/assets/images/marble-diagrams/delay.png b/docs_app/assets/images/marble-diagrams/delay.png new file mode 100644 index 0000000000..19a1d3bf55 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/delay.png differ diff --git a/docs_app/assets/images/marble-diagrams/delayWhen.png b/docs_app/assets/images/marble-diagrams/delayWhen.png new file mode 100644 index 0000000000..2513fed9b0 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/delayWhen.png differ diff --git a/docs_app/assets/images/marble-diagrams/dematerialize.png b/docs_app/assets/images/marble-diagrams/dematerialize.png new file mode 100644 index 0000000000..9e53d0ecd1 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/dematerialize.png differ diff --git a/docs_app/assets/images/marble-diagrams/distinctUntilChanged.png b/docs_app/assets/images/marble-diagrams/distinctUntilChanged.png new file mode 100644 index 0000000000..cdf124656b Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/distinctUntilChanged.png differ diff --git a/docs_app/assets/images/marble-diagrams/distinctUntilKeyChanged.png b/docs_app/assets/images/marble-diagrams/distinctUntilKeyChanged.png new file mode 100644 index 0000000000..ec8c481ec7 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/distinctUntilKeyChanged.png differ diff --git a/docs_app/assets/images/marble-diagrams/elementAt.png b/docs_app/assets/images/marble-diagrams/elementAt.png new file mode 100644 index 0000000000..969544093f Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/elementAt.png differ diff --git a/docs_app/assets/images/marble-diagrams/empty.png b/docs_app/assets/images/marble-diagrams/empty.png new file mode 100644 index 0000000000..ee035727ed Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/empty.png differ diff --git a/docs_app/assets/images/marble-diagrams/endWith.png b/docs_app/assets/images/marble-diagrams/endWith.png new file mode 100644 index 0000000000..a1cd41465a Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/endWith.png differ diff --git a/docs_app/assets/images/marble-diagrams/every.png b/docs_app/assets/images/marble-diagrams/every.png new file mode 100644 index 0000000000..f43e1ea009 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/every.png differ diff --git a/docs_app/assets/images/marble-diagrams/exhaust.png b/docs_app/assets/images/marble-diagrams/exhaust.png new file mode 100644 index 0000000000..341d0d121c Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/exhaust.png differ diff --git a/docs_app/assets/images/marble-diagrams/exhaustMap.png b/docs_app/assets/images/marble-diagrams/exhaustMap.png new file mode 100644 index 0000000000..4cdaa9fcae Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/exhaustMap.png differ diff --git a/docs_app/assets/images/marble-diagrams/expand.png b/docs_app/assets/images/marble-diagrams/expand.png new file mode 100644 index 0000000000..46dce3a7f1 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/expand.png differ diff --git a/docs_app/assets/images/marble-diagrams/filter.png b/docs_app/assets/images/marble-diagrams/filter.png new file mode 100644 index 0000000000..9f2755cb8a Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/filter.png differ diff --git a/docs_app/assets/images/marble-diagrams/find.png b/docs_app/assets/images/marble-diagrams/find.png new file mode 100644 index 0000000000..4f91f1b2fe Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/find.png differ diff --git a/docs_app/assets/images/marble-diagrams/findIndex.png b/docs_app/assets/images/marble-diagrams/findIndex.png new file mode 100644 index 0000000000..d405b9127f Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/findIndex.png differ diff --git a/docs_app/assets/images/marble-diagrams/first.png b/docs_app/assets/images/marble-diagrams/first.png new file mode 100644 index 0000000000..6be24baa29 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/first.png differ diff --git a/docs_app/assets/images/marble-diagrams/forkJoin.png b/docs_app/assets/images/marble-diagrams/forkJoin.png new file mode 100644 index 0000000000..5a4a6b39d8 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/forkJoin.png differ diff --git a/docs_app/assets/images/marble-diagrams/from.png b/docs_app/assets/images/marble-diagrams/from.png new file mode 100644 index 0000000000..74a8473045 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/from.png differ diff --git a/docs_app/assets/images/marble-diagrams/fromEvent.png b/docs_app/assets/images/marble-diagrams/fromEvent.png new file mode 100644 index 0000000000..499be0884a Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/fromEvent.png differ diff --git a/docs_app/assets/images/marble-diagrams/fromEventPattern.png b/docs_app/assets/images/marble-diagrams/fromEventPattern.png new file mode 100644 index 0000000000..f80e97361a Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/fromEventPattern.png differ diff --git a/docs_app/assets/images/marble-diagrams/generate.png b/docs_app/assets/images/marble-diagrams/generate.png new file mode 100644 index 0000000000..424c89e8ba Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/generate.png differ diff --git a/docs_app/assets/images/marble-diagrams/groupBy.png b/docs_app/assets/images/marble-diagrams/groupBy.png new file mode 100644 index 0000000000..65ee282602 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/groupBy.png differ diff --git a/docs_app/assets/images/marble-diagrams/ignoreElements.png b/docs_app/assets/images/marble-diagrams/ignoreElements.png new file mode 100644 index 0000000000..640f269763 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/ignoreElements.png differ diff --git a/docs_app/assets/images/marble-diagrams/interval.png b/docs_app/assets/images/marble-diagrams/interval.png new file mode 100644 index 0000000000..6d62bc0c73 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/interval.png differ diff --git a/docs_app/assets/images/marble-diagrams/isEmpty.png b/docs_app/assets/images/marble-diagrams/isEmpty.png new file mode 100644 index 0000000000..426f321aba Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/isEmpty.png differ diff --git a/docs_app/assets/images/marble-diagrams/last.png b/docs_app/assets/images/marble-diagrams/last.png new file mode 100644 index 0000000000..38bf947f54 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/last.png differ diff --git a/docs_app/assets/images/marble-diagrams/map.png b/docs_app/assets/images/marble-diagrams/map.png new file mode 100644 index 0000000000..74c3af550f Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/map.png differ diff --git a/docs_app/assets/images/marble-diagrams/mapTo.png b/docs_app/assets/images/marble-diagrams/mapTo.png new file mode 100644 index 0000000000..216f5e723e Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/mapTo.png differ diff --git a/docs_app/assets/images/marble-diagrams/materialize.png b/docs_app/assets/images/marble-diagrams/materialize.png new file mode 100644 index 0000000000..f3c41d01c3 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/materialize.png differ diff --git a/docs_app/assets/images/marble-diagrams/max.png b/docs_app/assets/images/marble-diagrams/max.png new file mode 100644 index 0000000000..7f3498e822 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/max.png differ diff --git a/docs_app/assets/images/marble-diagrams/merge.png b/docs_app/assets/images/marble-diagrams/merge.png new file mode 100644 index 0000000000..183fea0446 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/merge.png differ diff --git a/docs_app/assets/images/marble-diagrams/mergeAll.png b/docs_app/assets/images/marble-diagrams/mergeAll.png new file mode 100644 index 0000000000..f4658f559f Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/mergeAll.png differ diff --git a/docs_app/assets/images/marble-diagrams/mergeMap.png b/docs_app/assets/images/marble-diagrams/mergeMap.png new file mode 100644 index 0000000000..51f42cedc5 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/mergeMap.png differ diff --git a/docs_app/assets/images/marble-diagrams/mergeMapTo.png b/docs_app/assets/images/marble-diagrams/mergeMapTo.png new file mode 100644 index 0000000000..01d3446062 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/mergeMapTo.png differ diff --git a/docs_app/assets/images/marble-diagrams/min.png b/docs_app/assets/images/marble-diagrams/min.png new file mode 100644 index 0000000000..44464f782d Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/min.png differ diff --git a/docs_app/assets/images/marble-diagrams/multicast.png b/docs_app/assets/images/marble-diagrams/multicast.png new file mode 100644 index 0000000000..678394d8dd Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/multicast.png differ diff --git a/docs_app/assets/images/marble-diagrams/observeOn.png b/docs_app/assets/images/marble-diagrams/observeOn.png new file mode 100644 index 0000000000..77bcc432ff Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/observeOn.png differ diff --git a/docs_app/assets/images/marble-diagrams/of.png b/docs_app/assets/images/marble-diagrams/of.png new file mode 100644 index 0000000000..ae7b9c9f46 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/of.png differ diff --git a/docs_app/assets/images/marble-diagrams/onErrorResumeNext.png b/docs_app/assets/images/marble-diagrams/onErrorResumeNext.png new file mode 100644 index 0000000000..f7fca23f25 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/onErrorResumeNext.png differ diff --git a/docs_app/assets/images/marble-diagrams/pairs.png b/docs_app/assets/images/marble-diagrams/pairs.png new file mode 100644 index 0000000000..cae1014044 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/pairs.png differ diff --git a/docs_app/assets/images/marble-diagrams/pairwise.png b/docs_app/assets/images/marble-diagrams/pairwise.png new file mode 100644 index 0000000000..adb33e3b2e Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/pairwise.png differ diff --git a/docs_app/assets/images/marble-diagrams/partition.png b/docs_app/assets/images/marble-diagrams/partition.png new file mode 100644 index 0000000000..d536190af6 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/partition.png differ diff --git a/docs_app/assets/images/marble-diagrams/pluck.png b/docs_app/assets/images/marble-diagrams/pluck.png new file mode 100644 index 0000000000..2be8c15a41 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/pluck.png differ diff --git a/docs_app/assets/images/marble-diagrams/publish.png b/docs_app/assets/images/marble-diagrams/publish.png new file mode 100644 index 0000000000..70f95f05b3 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/publish.png differ diff --git a/docs_app/assets/images/marble-diagrams/publishBehavior.png b/docs_app/assets/images/marble-diagrams/publishBehavior.png new file mode 100644 index 0000000000..b1bad09e12 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/publishBehavior.png differ diff --git a/docs_app/assets/images/marble-diagrams/publishLast.png b/docs_app/assets/images/marble-diagrams/publishLast.png new file mode 100644 index 0000000000..d974ad938b Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/publishLast.png differ diff --git a/docs_app/assets/images/marble-diagrams/publishReplay.png b/docs_app/assets/images/marble-diagrams/publishReplay.png new file mode 100644 index 0000000000..a154e42d70 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/publishReplay.png differ diff --git a/docs_app/assets/images/marble-diagrams/range.png b/docs_app/assets/images/marble-diagrams/range.png new file mode 100644 index 0000000000..19d0fc1407 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/range.png differ diff --git a/docs_app/assets/images/marble-diagrams/reduce.png b/docs_app/assets/images/marble-diagrams/reduce.png new file mode 100644 index 0000000000..44f78f6bfd Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/reduce.png differ diff --git a/docs_app/assets/images/marble-diagrams/refCount.png b/docs_app/assets/images/marble-diagrams/refCount.png new file mode 100644 index 0000000000..9791c315ee Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/refCount.png differ diff --git a/docs_app/assets/images/marble-diagrams/repeat.png b/docs_app/assets/images/marble-diagrams/repeat.png new file mode 100644 index 0000000000..70533ad09d Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/repeat.png differ diff --git a/docs_app/assets/images/marble-diagrams/repeatWhen.png b/docs_app/assets/images/marble-diagrams/repeatWhen.png new file mode 100644 index 0000000000..6689bd4d66 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/repeatWhen.png differ diff --git a/docs_app/assets/images/marble-diagrams/retry.png b/docs_app/assets/images/marble-diagrams/retry.png new file mode 100644 index 0000000000..6938a35e29 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/retry.png differ diff --git a/docs_app/assets/images/marble-diagrams/retryWhen.png b/docs_app/assets/images/marble-diagrams/retryWhen.png new file mode 100644 index 0000000000..7bf94d533d Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/retryWhen.png differ diff --git a/docs_app/assets/images/marble-diagrams/sample.png b/docs_app/assets/images/marble-diagrams/sample.png new file mode 100644 index 0000000000..4f9f912c0b Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/sample.png differ diff --git a/docs_app/assets/images/marble-diagrams/sampleTime.png b/docs_app/assets/images/marble-diagrams/sampleTime.png new file mode 100644 index 0000000000..8978216166 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/sampleTime.png differ diff --git a/docs_app/assets/images/marble-diagrams/scan.png b/docs_app/assets/images/marble-diagrams/scan.png new file mode 100644 index 0000000000..88ce6c6e6a Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/scan.png differ diff --git a/docs_app/assets/images/marble-diagrams/sequenceEqual.png b/docs_app/assets/images/marble-diagrams/sequenceEqual.png new file mode 100644 index 0000000000..adc1fcee7a Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/sequenceEqual.png differ diff --git a/docs_app/assets/images/marble-diagrams/share.png b/docs_app/assets/images/marble-diagrams/share.png new file mode 100644 index 0000000000..1d83927718 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/share.png differ diff --git a/docs_app/assets/images/marble-diagrams/single.png b/docs_app/assets/images/marble-diagrams/single.png new file mode 100644 index 0000000000..de36ed6a3a Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/single.png differ diff --git a/docs_app/assets/images/marble-diagrams/skip.png b/docs_app/assets/images/marble-diagrams/skip.png new file mode 100644 index 0000000000..af430a288f Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/skip.png differ diff --git a/docs_app/assets/images/marble-diagrams/skipLast.png b/docs_app/assets/images/marble-diagrams/skipLast.png new file mode 100644 index 0000000000..7735871524 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/skipLast.png differ diff --git a/docs_app/assets/images/marble-diagrams/skipUntil.png b/docs_app/assets/images/marble-diagrams/skipUntil.png new file mode 100644 index 0000000000..a0146c9711 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/skipUntil.png differ diff --git a/docs_app/assets/images/marble-diagrams/skipWhile.png b/docs_app/assets/images/marble-diagrams/skipWhile.png new file mode 100644 index 0000000000..286a13d017 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/skipWhile.png differ diff --git a/docs_app/assets/images/marble-diagrams/startWith.png b/docs_app/assets/images/marble-diagrams/startWith.png new file mode 100644 index 0000000000..2ca6144bbb Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/startWith.png differ diff --git a/docs_app/assets/images/marble-diagrams/subscribeOn.png b/docs_app/assets/images/marble-diagrams/subscribeOn.png new file mode 100644 index 0000000000..2946fd34b7 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/subscribeOn.png differ diff --git a/docs_app/assets/images/marble-diagrams/switchAll.png b/docs_app/assets/images/marble-diagrams/switchAll.png new file mode 100644 index 0000000000..964893713d Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/switchAll.png differ diff --git a/docs_app/assets/images/marble-diagrams/switchMap.png b/docs_app/assets/images/marble-diagrams/switchMap.png new file mode 100644 index 0000000000..b313cc4d80 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/switchMap.png differ diff --git a/docs_app/assets/images/marble-diagrams/switchMapTo.png b/docs_app/assets/images/marble-diagrams/switchMapTo.png new file mode 100644 index 0000000000..8f7f442177 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/switchMapTo.png differ diff --git a/docs_app/assets/images/marble-diagrams/take.png b/docs_app/assets/images/marble-diagrams/take.png new file mode 100644 index 0000000000..4cc88ea96f Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/take.png differ diff --git a/docs_app/assets/images/marble-diagrams/takeLast.png b/docs_app/assets/images/marble-diagrams/takeLast.png new file mode 100644 index 0000000000..4f52c3ec4f Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/takeLast.png differ diff --git a/docs_app/assets/images/marble-diagrams/takeUntil.png b/docs_app/assets/images/marble-diagrams/takeUntil.png new file mode 100644 index 0000000000..f69241f123 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/takeUntil.png differ diff --git a/docs_app/assets/images/marble-diagrams/takeWhile.png b/docs_app/assets/images/marble-diagrams/takeWhile.png new file mode 100644 index 0000000000..5ab21ef300 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/takeWhile.png differ diff --git a/docs_app/assets/images/marble-diagrams/tap.png b/docs_app/assets/images/marble-diagrams/tap.png new file mode 100644 index 0000000000..72739499bc Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/tap.png differ diff --git a/docs_app/assets/images/marble-diagrams/throttle.png b/docs_app/assets/images/marble-diagrams/throttle.png new file mode 100644 index 0000000000..e778e6404a Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/throttle.png differ diff --git a/docs_app/assets/images/marble-diagrams/throttleTime.png b/docs_app/assets/images/marble-diagrams/throttleTime.png new file mode 100644 index 0000000000..5c0cc43ab8 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/throttleTime.png differ diff --git a/docs_app/assets/images/marble-diagrams/throw.png b/docs_app/assets/images/marble-diagrams/throw.png new file mode 100644 index 0000000000..d687a5bb0e Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/throw.png differ diff --git a/docs_app/assets/images/marble-diagrams/throwIfEmpty.png b/docs_app/assets/images/marble-diagrams/throwIfEmpty.png new file mode 100644 index 0000000000..062955df4d Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/throwIfEmpty.png differ diff --git a/docs_app/assets/images/marble-diagrams/timeInterval.png b/docs_app/assets/images/marble-diagrams/timeInterval.png new file mode 100644 index 0000000000..1f8ae1c817 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/timeInterval.png differ diff --git a/docs_app/assets/images/marble-diagrams/timeout.png b/docs_app/assets/images/marble-diagrams/timeout.png new file mode 100644 index 0000000000..9b79804e71 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/timeout.png differ diff --git a/docs_app/assets/images/marble-diagrams/timeoutWith.png b/docs_app/assets/images/marble-diagrams/timeoutWith.png new file mode 100644 index 0000000000..a15cd5efbd Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/timeoutWith.png differ diff --git a/docs_app/assets/images/marble-diagrams/timer.png b/docs_app/assets/images/marble-diagrams/timer.png new file mode 100644 index 0000000000..0870ae2763 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/timer.png differ diff --git a/docs_app/assets/images/marble-diagrams/timestamp.png b/docs_app/assets/images/marble-diagrams/timestamp.png new file mode 100644 index 0000000000..3f59e981d3 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/timestamp.png differ diff --git a/docs_app/assets/images/marble-diagrams/toArray.png b/docs_app/assets/images/marble-diagrams/toArray.png new file mode 100644 index 0000000000..21bbbc7672 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/toArray.png differ diff --git a/docs_app/assets/images/marble-diagrams/window.png b/docs_app/assets/images/marble-diagrams/window.png new file mode 100644 index 0000000000..2e22e4b9ad Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/window.png differ diff --git a/docs_app/assets/images/marble-diagrams/windowCount.png b/docs_app/assets/images/marble-diagrams/windowCount.png new file mode 100644 index 0000000000..b0a259cab9 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/windowCount.png differ diff --git a/docs_app/assets/images/marble-diagrams/windowTime.png b/docs_app/assets/images/marble-diagrams/windowTime.png new file mode 100644 index 0000000000..402e4e6b84 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/windowTime.png differ diff --git a/docs_app/assets/images/marble-diagrams/windowToggle.png b/docs_app/assets/images/marble-diagrams/windowToggle.png new file mode 100644 index 0000000000..fe14fa9b3b Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/windowToggle.png differ diff --git a/docs_app/assets/images/marble-diagrams/windowWhen.png b/docs_app/assets/images/marble-diagrams/windowWhen.png new file mode 100644 index 0000000000..70c9404da6 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/windowWhen.png differ diff --git a/docs_app/assets/images/marble-diagrams/withLatestFrom.png b/docs_app/assets/images/marble-diagrams/withLatestFrom.png new file mode 100644 index 0000000000..9c37244729 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/withLatestFrom.png differ diff --git a/docs_app/assets/images/marble-diagrams/zipAll.png b/docs_app/assets/images/marble-diagrams/zipAll.png new file mode 100644 index 0000000000..708d8783b9 Binary files /dev/null and b/docs_app/assets/images/marble-diagrams/zipAll.png differ diff --git a/docs_app/content/guide/operators.md b/docs_app/content/guide/operators.md new file mode 100644 index 0000000000..3864b91e34 --- /dev/null +++ b/docs_app/content/guide/operators.md @@ -0,0 +1,338 @@ +RxJS is mostly useful for its *operators*, even though the Observable is the foundation. Operators are the essential pieces that allow complex asynchronous code to be easily composed in a declarative manner. + +## What are operators? + +Operators are **functions**. There are two kinds of operators: + +**Pipeable Operators** are the kind that can be piped to Observables using the syntax `observableInstance.pipe(operator())`. These include, [`filter(...)`](/api/operators/filter), and [`mergeMap(...)`](/api/operators/mergeMap). When called, they do not *change* the existing Observable instance. Instead, they return a *new* Observable, whose subscription logic is based on the first Observable. + +A Pipeable Operator is a function that takes an Observable as its input and returns another Observable. It is a pure operation: the previous Observable stays unmodified. + +An Pipeable Operator is essentially a pure function which takes one Observable as input and generates another Observable as output. Subscribing to the output Observable will also subscribe to the input Observable. + +**Creation Operators** are the other kind of operator, which can be called as standalone functions to create a new Observable. For example: `of(1, 2, 3)` creates an observable that will emit 1, 2, and 3, one right after another. Creation operators will be discussed in more detail in a later section. + +For example, the operator called [`map`](/api/operators/map) is analogous to the Array method of the same name. Just as `[1, 2, 3].map(x => x * x)` will yield `[1, 4, 9]`, the Observable created like this: + +```ts +import { of } from 'rxjs'; +import { map } from 'rxjs/operators'; + +map(x => x * x)(of(1, 2, 3)).subscribe((v) => console.log(`value: ${v}`)); + +// Logs: +// value: 1 +// value: 4 +// value: 9 + +``` + +will emit `1`, `4`, `9`. Another useful operator is [`first`](/api/operators/first): + +```ts +import { of } from 'rxjs'; +import { first } from 'rxjs/operators'; + +first()(of(1, 2, 3).subscribe((v) => console.log(`value: ${v}`)); + +// Logs: +// value: 1 +``` + +Note that `map` logically must be constructed on the fly, since it must be given the mapping function to. By contrast, `first` could be a constant, but is nonetheless constructed on the fly. As a general practice, all operators are constructed, whether they need arguments or not. + +## Piping + +Pipeable operators are functions, so they *could* be used like ordinary functions: `op()(obs)` — but in practice, there tend to be many of them convolved together, and quickly become unreadable: `op4()(op3()(op2()(op1()(obs))))`. For that reason, Observables have a method called `.pipe()` that accomplishes the same thing while being much easier to read: + +```ts +obs.pipe( + op1(), + op2(), + op3(), + op3(), +) +``` + +As a stylistic matter, `op()(obs)` is never used, even if there is only one operator; `obs.pipe(op())` is universally preferred. + + +## Creation Operators + +**What are creation operators?** Distinct from pipeable operators, creation operators are functions that can be used to create an Observable with some common predefined behavior or by joining other Observables. + +A typical example of a creation operator would be the `interval` function. It takes a number (not an Observable) as input argument, and produces an Observable as output: + +```ts +import { interval } from 'rxjs'; + +const observable = interval(1000 /* number of milliseconds */); +``` + +See the list of [all static creation operators here](#creation-operators). + + +## Higher-order Observables + +Observables most commonly emit ordinary values like strings and numbers, but surprisingly often, it is necessary to handle Observables *of* Observables, so-called higher-order Observables. For example, imagine you had an Observable emitting strings that were the URLs of files you wanted to see. The code might look like this: + +```ts +const fileObservable = urlObservable.pipe( + map(url => http.get(url)), +); +``` + +`http.get()` returns an Observable (of string or string arrays probably) for each individual URL. Now you have an Observables *of* Observables, a higher-order Observable. + +But how do you work with a higher-order Observable? Typically, by _flattening_: by (somehow) converting a higher-order Observable into an ordinary Observable. For example: + +```ts +const fileObservable = urlObservable.pipe( + map(url => http.get(url)), + concatAll(), +); +``` + +The [`concatAll()`](/api/operators/concatAll) operator subscribes to each "inner" Observable that comes out of the "outer" Observable, and copies all the emitted values until that Observable completes, and goes on to the next one. All of the values are in that way concatenated. Other useful flattening operators (called [*join operators*](#join-operators)) are + +* [`mergeAll()`](/api/operators/mergeAll) — subscribes to each inner Observable as it arrives, then emits each value as it arrives +* [`switchAll()`](/api/operators/switchAll) — subscribes to the first inner Observable when it arrives, and emits each value as it arrives, but when the next inner Observable arrives, unsubscribes to the previous one, and subscribes to the new one. +* [`exhaust()`](/api/operators/exhaust) — subscribes to the first inner Observable when it arrives, and emits each value as it arrives, discarding all newly arriving inner Observables until that first one completes, then waits for the next inner Observable. + +Just as many array library combine [`map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) and [`flat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) (or `flatten()`) into a single [`flatMap()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap), there are mapping equivalents of all the RxJS flattening operators [`concatMap()`](/api/operators/concatMap), [`mergeMap()`](/api/operators/mergeMap), [`switchMap()`](/api/operators/switchMap), and [`exhaustMap()`](/api/operators/exhaustMap). + + + +## Marble diagrams + +To explain how operators work, textual descriptions are often not enough. Many operators are related to time, they may for instance delay, sample, throttle, or debounce value emissions in different ways. Diagrams are often a better tool for that. *Marble Diagrams* are visual representations of how operators work, and include the input Observable(s), the operator and its parameters, and the output Observable. + +In a marble diagram, time flows to the right, and the diagram describes how values ("marbles") are emitted on the Observable execution. + +Below you can see the anatomy of a marble diagram. + + + +Throughout this documentation site, we extensively use marble diagrams to explain how operators work. They may be really useful in other contexts too, like on a whiteboard or even in our unit tests (as ASCII diagrams). + +## Categories of operators + +There are operators for different purposes, and they may be categorized as: creation, transformation, filtering, joining, multicasting, error handling, utility, etc. In the following list you will find all the operators organized in categories. + +For a complete overview, see the [references page](/api). + +### Creation Operators + +- [`ajax`](/api/ajax/ajax) +- [`bindCallback`](/api/index/function/bindCallback) +- [`bindNodeCallback`](/api/index/function/bindNodeCallback) +- [`defer`](/api/index/function/defer) +- [`empty`](/api/index/function/empty) +- [`from`](/api/index/function/from) +- [`fromEvent`](/api/index/function/fromEvent) +- [`fromEventPattern`](/api/index/function/fromEventPattern) +- [`generate`](/api/index/function/generate) +- [`interval`](/api/index/function/interval) +- [`of`](/api/index/function/of) +- [`range`](/api/index/function/range) +- [`throwError`](/api/index/function/throwError) +- [`timer`](/api/index/function/timer) +- [`iif`](/api/index/function/iif) + +### Join Creation Operators +These are Observable creation operators that also have join functionality -- emitting values of multiple source Observables. + +- [`combineLatest`](/api/index/function/combineLatest) +- [`concat`](/api/index/function/concat) +- [`forkJoin`](/api/index/function/forkJoin) +- [`merge`](/api/index/function/merge) +- [`race`](/api/index/function/race) +- [`zip`](/api/index/function/zip) + +### Transformation Operators + +- [`buffer`](/api/operators/buffer) +- [`bufferCount`](/api/operators/bufferCount) +- [`bufferTime`](/api/operators/bufferTime) +- [`bufferToggle`](/api/operators/bufferToggle) +- [`bufferWhen`](/api/operators/bufferWhen) +- [`concatMap`](/api/operators/concatMap) +- [`concatMapTo`](/api/operators/concatMapTo) +- [`exhaust`](/api/operators/exhaust) +- [`exhaustMap`](/api/operators/exhaustMap) +- [`expand`](/api/operators/expand) +- [`groupBy`](/api/operators/groupBy) +- [`map`](/api/operators/map) +- [`mapTo`](/api/operators/mapTo) +- [`mergeMap`](/api/operators/mergeMap) +- [`mergeMapTo`](/api/operators/mergeMapTo) +- [`mergeScan`](/api/operators/mergeScan) +- [`pairwise`](/api/operators/pairwise) +- [`partition`](/api/operators/partition) +- [`pluck`](/api/operators/pluck) +- [`scan`](/api/operators/scan) +- [`switchMap`](/api/operators/switchMap) +- [`switchMapTo`](/api/operators/switchMapTo) +- [`window`](/api/operators/window) +- [`windowCount`](/api/operators/windowCount) +- [`windowTime`](/api/operators/windowTime) +- [`windowToggle`](/api/operators/windowToggle) +- [`windowWhen`](/api/operators/windowWhen) + +### Filtering Operators + +- [`audit`](/api/operators/audit) +- [`auditTime`](/api/operators/auditTime) +- [`debounce`](/api/operators/debounce) +- [`debounceTime`](/api/operators/debounceTime) +- [`distinct`](/api/operators/distinct) +- [`distinctKey`](../class/es6/Observable.js~Observable.html#instance-method-distinctKey) +- [`distinctUntilChanged`](/api/operators/distinctUntilChanged) +- [`distinctUntilKeyChanged`](/api/operators/distinctUntilKeyChanged) +- [`elementAt`](/api/operators/elementAt) +- [`filter`](/api/operators/filter) +- [`first`](/api/operators/first) +- [`ignoreElements`](/api/operators/ignoreElements) +- [`last`](/api/operators/last) +- [`sample`](/api/operators/sample) +- [`sampleTime`](/api/operators/sampleTime) +- [`single`](/api/operators/single) +- [`skip`](/api/operators/skip) +- [`skipLast`](/api/operators/skipLast) +- [`skipUntil`](/api/operators/skipUntil) +- [`skipWhile`](/api/operators/skipWhile) +- [`take`](/api/operators/take) +- [`takeLast`](/api/operators/takeLast) +- [`takeUntil`](/api/operators/takeUntil) +- [`takeWhile`](/api/operators/takeWhile) +- [`throttle`](/api/operators/throttle) +- [`throttleTime`](/api/operators/throttleTime) + +### Join Operators +Also see the [Join Creation Operators](#join-creation-operators) section above. + +- [`combineAll`](/api/operators/combineAll) +- [`concatAll`](/api/operators/concatAll) +- [`exhaust`](/api/operators/exhaust) +- [`mergeAll`](/api/operators/mergeAll) +- [`startWith`](/api/operators/startWith) +- [`withLatestFrom`](/api/operators/withLatestFrom) + +### Multicasting Operators + +- [`multicast`](/api/operators/multicast) +- [`publish`](/api/operators/publish) +- [`publishBehavior`](/api/operators/publishBehavior) +- [`publishLast`](/api/operators/publishLast) +- [`publishReplay`](/api/operators/publishReplay) +- [`share`](/api/operators/share) + +### Error Handling Operators + +- [`catchError`](/api/operators/catchError) +- [`retry`](/api/operators/retry) +- [`retryWhen`](/api/operators/retryWhen) + +### Utility Operators + +- [`tap`](/api/operators/tap) +- [`delay`](/api/operators/delay) +- [`delayWhen`](/api/operators/delayWhen) +- [`dematerialize`](/api/operators/dematerialize) +- [`materialize`](/api/operators/materialize) +- [`observeOn`](/api/operators/observeOn) +- [`subscribeOn`](/api/operators/subscribeOn) +- [`timeInterval`](/api/operators/timeInterval) +- [`timestamp`](/api/operators/timestamp) +- [`timeout`](/api/operators/timeout) +- [`timeoutWith`](/api/operators/timeoutWith) +- [`toArray`](/api/operators/toArray) + +### Conditional and Boolean Operators + +- [`defaultIfEmpty`](/api/operators/defaultIfEmpty) +- [`every`](/api/operators/every) +- [`find`](/api/operators/find) +- [`findIndex`](/api/operators/findIndex) +- [`isEmpty`](/api/operators/isEmpty) + +### Mathematical and Aggregate Operators + +- [`count`](/api/operators/count) +- [`max`](/api/operators/max) +- [`min`](/api/operators/min) +- [`reduce`](/api/operators/reduce) + + + +## Creating custom observables + +### Use the `pipe()` function to make new operators + +If there is a commonly used sequence of operators in your code, use the `pipe()` function to extract the sequence into a new operator. Even if a sequence is not that common, breaking it out into a single operator can improve readability. + +For example, you could make a function that discarded odd values and doubled even values like this: + +```ts +import { pipe } from 'rxjs'; +import { filter, map } from 'rxjs'; + +function discardOddDoubleEven() { + return pipe( + filter(v => ! (v % 2)), + map(v => v + v), + ); +} +``` + +(The `pipe()` function is analogous to, but not the same thing as, the `.pipe()` method on an Observable.) + +### Creating new operators from scratch + +It is more complicated, but if you have to write an operator that cannot be made from a combination of existing operators (a rare occurrance), you can write an operator from scratch using the Observable constructor, like this: + + +```ts +import { Observable } from 'rxjs'; + +function delay(delayInMillis) { + return (observable) => new Observable(observer => { + // this function will called each time this + // Observable is subscribed to. + const allTimerIDs = new Set(); + const subscription = observable.subscribe({ + next(value) { + const timerID = setTimeout(() => { + observer.next(value); + allTimerIDs.delete(timerID); + }, delayInMillis); + allTimerIDs.add(timerID); + }, + error(err) { + observer.error(err); + }, + complete() { + observer.complete(); + } + }); + // the return value is the teardown function, + // which will be invoked when the new + // Observable is unsubscribed from. + return () => { + subscription.unsubscribe(); + allTimerIDs.forEach(timerID => { + clearTimeout(timerID); + }); + } + }); +} +``` + +Note that you must + +1. implement all three Observer functions, `next()`, `error()`, and `complete()` when subscribing to the input Observable. +2. implement a "teardown" function that cleans up when the Observable completes (in this case by unsubscribing and clearing any pending timeouts). +3. return that teardown function from the function passed to the Observable constructor. + +Of course, this is only an example; the `delay()` operator [already exists](/api/operators/delay). + diff --git a/docs_app/content/guide/overview.md b/docs_app/content/guide/overview.md index 0d661e6ff4..959f58c501 100644 --- a/docs_app/content/guide/overview.md +++ b/docs_app/content/guide/overview.md @@ -18,56 +18,55 @@ The essential concepts in RxJS which solve async event management are: ## First examples Normally you register event listeners. -```js -const button = document.querySelector('button'); -button.addEventListener('click', () => console.log('Clicked!')); + +```ts +document.addEventListener('click', () => console.log('Clicked!')); ``` Using RxJS you create an observable instead. -```js -const { fromEvent } = rxjs; -const button = document.querySelector('button'); -fromEvent(button, 'click') - .subscribe(() => console.log('Clicked!')); -``` +```ts +import { fromEvent } from 'rxjs'; +fromEvent(document, 'click').subscribe(() => console.log('Clicked!')); +``` ### Purity + What makes RxJS powerful is its ability to produce values using pure functions. That means your code is less prone to errors. Normally you would create an impure function, where other pieces of your code can mess up your state. -```js -var count = 0; -var button = document.querySelector('button'); -button.addEventListener('click', () => console.log(`Clicked ${++count} times`)); + +```ts +let count = 0; +document.addEventListener('click', () => console.log(`Clicked ${++count} times`)); ``` Using RxJS you isolate the state. -```Js -const { fromEvent } = rxjs; -const { scan } = rxjs.operators; - -const button = document.querySelector('button'); -fromEvent(button, 'click').pipe( - scan(count => count + 1, 0) -) -.subscribe(count => console.log(`Clicked ${count} times`)); + +```ts +import { fromEvent } from 'rxjs'; +import { scan } from 'rxjs/operators'; + +fromEvent(document, 'click') + .pipe(scan(count => count + 1, 0)) + .subscribe(count => console.log(`Clicked ${count} times`)); ``` The **scan** operator works just like **reduce** for arrays. It takes a value which is exposed to a callback. The returned value of the callback will then become the next value exposed the next time the callback runs. ### Flow + RxJS has a whole range of operators that helps you control how the events flow through your observables. This is how you would allow at most one click per second, with plain JavaScript: -```js -var count = 0; -var rate = 1000; -var lastClick = Date.now() - rate; -var button = document.querySelector('button'); -button.addEventListener('click', () => { + +```ts +let count = 0; +let rate = 1000; +let lastClick = Date.now() - rate; +document.addEventListener('click', () => { if (Date.now() - lastClick >= rate) { console.log(`Clicked ${++count} times`); lastClick = Date.now(); @@ -76,50 +75,53 @@ button.addEventListener('click', () => { ``` With RxJS: -```js -const { fromEvent } = rxjs; -const { throttleTime, scan } = rxjs.operators; - -const button = document.querySelector('button'); -fromEvent(button, 'click').pipe( - throttleTime(1000), - scan(count => count + 1, 0) -) -.subscribe(count => console.log(`Clicked ${count} times`)); + +```ts +import { fromEvent } from 'rxjs'; +import { throttleTime, scan } from 'rxjs/operators'; + +fromEvent(document, 'click') + .pipe( + throttleTime(1000), + scan(count => count + 1, 0) + ) + .subscribe(count => console.log(`Clicked ${count} times`)); ``` Other flow control operators are [**filter**](../api/operators/filter), [**delay**](../api/operators/delay), [**debounceTime**](../api/operators/debounceTime), [**take**](../api/operators/take), [**takeUntil**](../api/operators/takeUntil), [**distinct**](../api/operators/distinct), [**distinctUntilChanged**](../api/operators/distinctUntilChanged) etc. ### Values + You can transform the values passed through your observables. Here's how you can add the current mouse x position for every click, in plain JavaScript: -```js + +```ts let count = 0; const rate = 1000; let lastClick = Date.now() - rate; -const button = document.querySelector('button'); -button.addEventListener('click', (event) => { +document.addEventListener('click', event => { if (Date.now() - lastClick >= rate) { count += event.clientX; - console.log(count) + console.log(count); lastClick = Date.now(); } }); ``` With RxJS: -```js -const { fromEvent } = rxjs; -const { throttleTime, map, scan } = rxjs.operators; - -const button = document.querySelector('button'); -fromEvent(button, 'click').pipe( - throttleTime(1000), - map(event => event.clientX), - scan((count, clientX) => count + clientX, 0) -) -.subscribe(count => console.log(count)); + +```ts +import { fromEvent } from 'rxjs'; +import { throttleTime, map, scan } from 'rxjs/operators'; + +fromEvent(document, 'click') + .pipe( + throttleTime(1000), + map(event => event.clientX), + scan((count, clientX) => count + clientX, 0) + ) + .subscribe(count => console.log(count)); ``` Other value producing operators are [**pluck**](../api/operators/pluck), [**pairwise**](../api/operators/pairwise), [**sample**](../api/operators/sample) etc. diff --git a/docs_app/content/guide/testing/internal-marble-tests.md b/docs_app/content/guide/testing/internal-marble-tests.md index a703e0442b..11441e25fc 100644 --- a/docs_app/content/guide/testing/internal-marble-tests.md +++ b/docs_app/content/guide/testing/internal-marble-tests.md @@ -15,7 +15,7 @@ André Staltz first recommended this as a DSL for creating unit tests, and it ha ## See also -- [Code of Conduct](./guide/code-of-conduct) +- [Code of Conduct](../../code-of-conduct) ## Basic methods diff --git a/docs_app/content/guide/testing/marble-testing.md b/docs_app/content/guide/testing/marble-testing.md index e0203a6f0e..61b2b2a98e 100644 --- a/docs_app/content/guide/testing/marble-testing.md +++ b/docs_app/content/guide/testing/marble-testing.md @@ -12,7 +12,7 @@ We can test our _asynchronous_ RxJS code _synchronously_ and deterministically b ```ts import { TestScheduler } from 'rxjs/testing'; -const scheduler = new TestScheduler> ((actual, expected) => { +const scheduler = new TestScheduler((actual, expected) => { // asserting the two objects are equal // e.g. using chai. expect(actual).deep.equal(expected); @@ -49,9 +49,11 @@ testScheduler.run(helpers => { }); ``` +Although `run()` executes entirely synchronously, the helper functions inside your callback function do not! These functions **schedule assertions** that will execute either when your callback completes or when you explicitly call `flush()`. Be wary of calling synchronous assertions, for example `expect` from your testing library of choice, from within the callback. + - `hot(marbleDiagram: string, values?: object, error?: any)` - creates a ["hot" observable](https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339) (like a subject) that will behave as though it's already "running" when the test begins. An interesting difference is that `hot` marbles allow a `^` character to signal where the "zero frame" is. That is the point at which the subscription to observables being tested begins. - `cold(marbleDiagram: string, values?: object, error?: any)` - creates a ["cold" observable](https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339) whose subscription starts when the test begins. -- `expectObservable(actual: Observable).toBe(marbleDiagram: string, values?: object, error?: any)` - schedules an assertion for when the TestScheduler flushes. Give `subscriptionMarbles` as parameter to change the schedule of subscription and unsubscription. If you don't provide the `subscriptionMarbles` parameter it will subscribe at the beginning and never unsubscribe. Read below about subscription marble diagram. +- `expectObservable(actual: Observable, subscriptionMarbles?: string).toBe(marbleDiagram: string, values?: object, error?: any)` - schedules an assertion for when the TestScheduler flushes. Give `subscriptionMarbles` as parameter to change the schedule of subscription and unsubscription. If you don't provide the `subscriptionMarbles` parameter it will subscribe at the beginning and never unsubscribe. Read below about subscription marble diagram. - `expectSubscriptions(actualSubscriptionLogs: SubscriptionLog[]).toBe(subscriptionMarbles: string)` - like `expectObservable` schedules an assertion for when the testScheduler flushes. Both `cold()` and `hot()` return an observable with a property `subscriptions` of type `SubscriptionLog[]`. Give `subscriptions` as parameter to `expectSubscriptions` to assert whether it matches the `subscriptionsMarbles` marble diagram given in `toBe()`. Subscription marble diagrams are slightly different than Observable marble diagrams. Read more below. - `flush()` - immediately starts virtual time. Not often used since `run()` will automatically flush for you when your callback returns, but in some cases you may wish to flush more than once or otherwise have more control. @@ -68,7 +70,26 @@ How many virtual milliseconds one frame represents depends on the value of `Test - `[0-9]+[ms|s|m]` time progression: the time progression syntax lets you progress virtual time by a specific amount. It's a number, followed by a time unit of `ms` (milliseconds), `s` (seconds), or `m` (minutes) without any space between them, e.g. `a 10ms b`. See [Time progression syntax](#time-progression-syntax) for more details. - `'|'` complete: The successful completion of an observable. This is the observable producer signaling `complete()`. - `'#'` error: An error terminating the observable. This is the observable producer signaling `error()`. -- `[a-z0-9]` e.g. `'a'` any alphanumeric character: Represents a value being emitted by the producer signaling `next()`. +- `[a-z0-9]` e.g. `'a'` any alphanumeric character: Represents a value being emitted by the producer signaling `next()`. Also consider that you could map this into an object or an array like this: +```ts + const expected = '400ms (a-b|)'; + const values = { + a: 'value emitted', + b: 'another value emitter', + }; + + expectObservable(someStreamForTesting) + .toBe(expected, values); + // This would work also + const expected = '400ms (0-1|)'; + const values = [ + 'value emitted', + 'another value emitted', + ]; + + expectObservable(someStreamForTesting) + .toBe(expected, values); +``` - `'()'` sync groupings: When multiple events need to be in the same frame synchronously, parentheses are used to group those events. You can group next'd values, a completion, or an error in this manner. The position of the initial `(` determines the time at which its values are emitted. While it can be unintuitive at first, after all the values have synchronously emitted time will progress a number of frames equal to the number of ASCII characters in the group, including the parentheses. e.g. `'(abc)'` will emit the values of a, b, and c synchronously in the same frame and then advance virtual time by 5 frames, `'(abc)'.length === 5`. This is done because it often helps you vertically align your marble diagrams, but it's a known pain point in real-world testing. [Learn more about known issues](#known-issues). - `'^'` subscription point: (hot observables only) shows the point at which the tested observables will be subscribed to the hot observable. This is the "zero frame" for that observable, every frame before the `^` will be negative. Negative time might seem pointless, but there are in fact advanced cases where this is necessary, usually involving ReplaySubjects. @@ -123,13 +144,15 @@ expectObservable(result).toBe(expected); `'-----(a|)'`: on frame 5 emit `a` and `complete`. -`'a 9ms b 9s c'`: on frame 0 emit `a`, on frame 10 emit `b`, on frame 10,012 emit `c`, then on on frame 10,013 `complete`. +`'a 9ms b 9s c|'`: on frame 0 emit `a`, on frame 10 emit `b`, on frame 10,012 emit `c`, then on on frame 10,013 `complete`. `'--a 2.5m b'`: on frame 2 emit `a`, on frame 150,003 emit `b` and never complete. ## Subscription Marbles -The `expectSubscriptions` helper allows you to assert that a `cold()` or `hot()` Observable you created was subscribed/unsubscribed to at the correct point in time. The subscription marble syntax is slightly different to conventional marble syntax. +The `expectSubscriptions` helper allows you to assert that a `cold()` or `hot()` Observable you created was subscribed/unsubscribed to at the correct point in time. The `subscriptionMarbles` parameter to `expectObservable` allows your test to defer subscription to a later virtual time, and/or unsubscribe even if the observable being tested has not yet completed. + +The subscription marble syntax is slightly different to conventional marble syntax. - `'-'` time: 1 frame time passing. - `[0-9]+[ms|s|m]` time progression: the time progression syntax lets you progress virtual time by a specific amount. It's a number, followed by a time unit of `ms` (milliseconds), `s` (seconds), or `m` (minutes) without any space between them, e.g. `a 10ms b`. See [Time progression syntax](#time-progression-syntax) for more details. @@ -148,6 +171,37 @@ There should be **at most one** `^` point in a subscription marble diagram, and `'500ms ^ 1s !'`: on frame 500 a subscription happened, and on frame 1,501 was unsubscribed. +Given a hot source, test multiple subscribers that subscribe at different times: + +```js +testScheduler.run(({ hot, expectObservable }) => { + const source = hot('--a--a--a--a--a--a--a--'); + const sub1 = ' --^-----------!'; + const sub2 = ' ---------^--------!'; + const expect1 = ' --a--a--a--a--'; + const expect2 = ' -----------a--a--a-'; + expectObservable(source, sub1).toBe(expect1); + expectObservable(source, sub2).toBe(expect2); +}); +``` + +Manually unsubscribe from a source that will never complete: + +```js +it('should repeat forever', () => { + const scheduler = createScheduler(); + + scheduler.run(({ expectObservable }) => { + const foreverStream$ = interval(1).pipe(mapTo('a')); + + // Omitting this arg may crash the test suite. + const unsub = '------ !'; + + expectObservable(foreverStream$, unsub).toBe('-aaaaa'); + }); +}); +``` + *** ## Known Issues diff --git a/docs_app/content/navigation.json b/docs_app/content/navigation.json index 4234206ae1..3e686db72c 100644 --- a/docs_app/content/navigation.json +++ b/docs_app/content/navigation.json @@ -25,11 +25,15 @@ "title": "Overview", "tooltip": "RxJS Overview", "children": [ - { + { "url": "guide/observable", "title": "Observables" - }, - { + }, + { + "url": "guide/operators", + "title": "Operators" + }, + { "url": "guide/subscription", "title": "Subscription" }, @@ -37,6 +41,10 @@ "url": "guide/subject", "title": "Subjects" }, + { + "url": "guide/operators", + "title": "Operators" + }, { "url": "guide/scheduler", "title": "Scheduler" @@ -47,6 +55,10 @@ { "url": "guide/testing/marble-testing", "title": "Marble Testing" + }, + { + "url": "guide/testing/internal-marble-tests", + "title": "Contribute tests to RxJS" } ] } @@ -60,18 +72,12 @@ { "url": "api", "title": "Reference", - "tooltip": "RxJS Reference", - "children": [ - { - "url": "api", - "title": "API", - "tooltip": "RxJS Reference" - }, - { - "url": "operator-decision-tree", - "title": "Operator Decision Tree" - } - ] + "tooltip": "RxJS Reference" + }, + { + "tooltip": "Operator Decision Tree", + "url": "operator-decision-tree", + "title": "Operator Decision Tree" }, { "title": "About Version 6", diff --git a/docs_app/content/external-resources.md b/docs_app/content/resources.md similarity index 98% rename from docs_app/content/external-resources.md rename to docs_app/content/resources.md index 1ee94df676..0827e6c4ce 100644 --- a/docs_app/content/external-resources.md +++ b/docs_app/content/resources.md @@ -4,8 +4,6 @@ This is a collection of resources that can be found to help with the learning pr **Note**: RxJS maintainers are not responsible for any of the following content that is not part of the official rxjs documentation site. Please adhere to the terms and copyrights of the sites and links listed. ---- - ## Blogs ### 2018 @@ -13,20 +11,17 @@ This is a collection of resources that can be found to help with the learning pr * Thinking reactive with the SIP principle | [StrongBrew](https://blog.strongbrew.io/the-sip-principle/) | June 30, 2018 * Angular 6: Upgrading API calls to RxJS 6 | [Metal Toad](https://www.metaltoad.com/blog/angular-6-upgrading-api-calls-rxjs-6) | May 23, 2018 ---- ## Playgrounds * Rx Visualizer | [https://rxviz.com/](https://rxviz.com/) ---- ## Reference * Learn RxJS | [https://www.learnrxjs.io/](https://www.learnrxjs.io) * [http://reactive.how](http://reactive.how) ---- ## Tutorials @@ -34,7 +29,6 @@ This is a collection of resources that can be found to help with the learning pr * A Better Way to Learn RxJS & Observables | [thinkster](https://thinkster.io/tutorials/learn-rxjs-observables) | August 18, 2018 ---- ## Videos diff --git a/docs_app/src/app/custom-elements/api/api-list.component.html b/docs_app/src/app/custom-elements/api/api-list.component.html index 6329175b6e..219cab5078 100644 --- a/docs_app/src/app/custom-elements/api/api-list.component.html +++ b/docs_app/src/app/custom-elements/api/api-list.component.html @@ -25,7 +25,10 @@

{{section.title}}

diff --git a/docs_app/src/app/custom-elements/api/api-list.component.spec.ts b/docs_app/src/app/custom-elements/api/api-list.component.spec.ts index 505fdf4dc9..2f691d304f 100644 --- a/docs_app/src/app/custom-elements/api/api-list.component.spec.ts +++ b/docs_app/src/app/custom-elements/api/api-list.component.spec.ts @@ -97,7 +97,7 @@ describe('ApiListComponent', () => { }); }); - describe('initial critera from location', () => { + describe('initial criteria from location', () => { let locationService: TestLocationService; beforeEach(() => { @@ -110,7 +110,7 @@ describe('ApiListComponent', () => { component.filteredSections.subscribe(filtered => { expect(filtered.length).toBe(1, 'sections'); expect(filtered[0].name).toBe(section, 'section name'); - const items = filtered[0].items.filter(item => item.show); + const items = filtered[0].items.filter(currentItem => currentItem.show); expect(items.length).toBe(1, 'items'); const item = items[0]; @@ -198,6 +198,31 @@ describe('ApiListComponent', () => { expect(search.type).toBe('class'); }); }); + + describe('item stability rendering', () => { + + beforeEach(() => { + fixture.detectChanges(); + }); + + function expectRenderedStability(path: string, title: string, classes: string) { + const apiListElement: HTMLElement = fixture.nativeElement; + const a = apiListElement.querySelector(`a[href="${path}"] ${classes}`) as HTMLElement; + expect((a.textContent as string).trim()).toEqual(title.trim()); + } + + it('should display stable', () => { + expectRenderedStability('api/common/class_2', 'Class 2', '.stability'); + }); + + it('should display experimental', () => { + expectRenderedStability('api/common/class_1', 'Class 1 (experimental)', '.stability.experimental'); + }); + + it('should display deprecated', () => { + expectRenderedStability('api/core/function_1', 'Function 1 (deprecated)', '.stability.deprecated'); + }); + }); }); ////// Helpers //////// @@ -268,7 +293,7 @@ const apiSections: ApiSection[] = [ { "name": "function_1", "title": "Function 1", - "path": "api/core/function 1", + "path": "api/core/function_1", "docType": "function", "stability": "deprecated", "securityRisk": true diff --git a/docs_app/src/app/search/search-worker.js b/docs_app/src/app/search/search-worker.js index 503a75aaa8..d29744c608 100644 --- a/docs_app/src/app/search/search-worker.js +++ b/docs_app/src/app/search/search-worker.js @@ -28,6 +28,7 @@ self.onmessage = handleMessage; // the path and search terms for a page function createIndex(addFn) { return lunr(/** @this */function() { + this.pipeline.remove(lunr.stopWordFilter); this.ref('path'); this.field('titleWords', {boost: 100}); this.field('headingWords', {boost: 50}); diff --git a/docs_app/src/assets/images/guide/marble-diagram-anatomy.svg b/docs_app/src/assets/images/guide/marble-diagram-anatomy.svg new file mode 100644 index 0000000000..0cf645a3e1 --- /dev/null +++ b/docs_app/src/assets/images/guide/marble-diagram-anatomy.svg @@ -0,0 +1,140 @@ + + + marble-diagram-anatomy + + + + + + + multiplyByTen + + + + + + + + + + + + + + + + 40 + + + + + + 60 + + + + + + + + + + + + + + 4 + + + + + + 6 + + + + + + a + + + + + + 8 + + + + + + + + This is time flowing from + left to right to represent + the execution of the + input Observable. + + + + + These are values emitted + by the Observable. + + + + + + + + + + + + + This vertical line represents + the "complete" notification + and indicates that the + Observable has completed + successfully. + + + + + + + This box indicates the + operator which takes + the input Observable + (above) to produce the + output Observable + (below). The text inside + the box shows the + nature of the + transformation. + + + + + + + This Observable is the + output of the operator + call. + + + + + + + This X represents an error emitted by + the output Observable, indicating + abnormal termination. Neither values + nor the vertical will be delivered + thereafter. + + + + + + + \ No newline at end of file diff --git a/docs_app/src/assets/images/marble-diagrams/NEVER.png b/docs_app/src/assets/images/marble-diagrams/NEVER.png new file mode 100644 index 0000000000..d6c8b526d0 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/NEVER.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/audit.png b/docs_app/src/assets/images/marble-diagrams/audit.png new file mode 100644 index 0000000000..c0e2e01419 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/audit.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/auditTime.png b/docs_app/src/assets/images/marble-diagrams/auditTime.png new file mode 100644 index 0000000000..ad78ffccba Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/auditTime.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/buffer.png b/docs_app/src/assets/images/marble-diagrams/buffer.png new file mode 100644 index 0000000000..0109fc9cd3 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/buffer.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/bufferCount.png b/docs_app/src/assets/images/marble-diagrams/bufferCount.png new file mode 100644 index 0000000000..b29274e629 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/bufferCount.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/bufferTime.png b/docs_app/src/assets/images/marble-diagrams/bufferTime.png new file mode 100644 index 0000000000..62a6a22ab9 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/bufferTime.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/bufferToggle.png b/docs_app/src/assets/images/marble-diagrams/bufferToggle.png new file mode 100644 index 0000000000..f355da97c7 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/bufferToggle.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/bufferWhen.png b/docs_app/src/assets/images/marble-diagrams/bufferWhen.png new file mode 100644 index 0000000000..4c618ec356 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/bufferWhen.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/catch.png b/docs_app/src/assets/images/marble-diagrams/catch.png new file mode 100644 index 0000000000..e08b6c0c9c Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/catch.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/combineAll.png b/docs_app/src/assets/images/marble-diagrams/combineAll.png new file mode 100644 index 0000000000..211af1e256 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/combineAll.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/combineLatest.png b/docs_app/src/assets/images/marble-diagrams/combineLatest.png new file mode 100644 index 0000000000..67ebd5a011 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/combineLatest.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/concat.png b/docs_app/src/assets/images/marble-diagrams/concat.png new file mode 100644 index 0000000000..8dd83696b1 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/concat.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/concatAll.png b/docs_app/src/assets/images/marble-diagrams/concatAll.png new file mode 100644 index 0000000000..968d47e09b Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/concatAll.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/concatMap.png b/docs_app/src/assets/images/marble-diagrams/concatMap.png new file mode 100644 index 0000000000..137cf4def6 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/concatMap.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/concatMapTo.png b/docs_app/src/assets/images/marble-diagrams/concatMapTo.png new file mode 100644 index 0000000000..226878800a Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/concatMapTo.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/count.png b/docs_app/src/assets/images/marble-diagrams/count.png new file mode 100644 index 0000000000..ae9b81071e Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/count.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/create.png b/docs_app/src/assets/images/marble-diagrams/create.png new file mode 100644 index 0000000000..f329eaa6e8 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/create.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/debounce.png b/docs_app/src/assets/images/marble-diagrams/debounce.png new file mode 100644 index 0000000000..567554dad9 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/debounce.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/debounceTime.png b/docs_app/src/assets/images/marble-diagrams/debounceTime.png new file mode 100644 index 0000000000..864328a073 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/debounceTime.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/defaultIfEmpty.png b/docs_app/src/assets/images/marble-diagrams/defaultIfEmpty.png new file mode 100644 index 0000000000..d56f1ff4f8 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/defaultIfEmpty.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/defer.png b/docs_app/src/assets/images/marble-diagrams/defer.png new file mode 100644 index 0000000000..6853738137 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/defer.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/delay.png b/docs_app/src/assets/images/marble-diagrams/delay.png new file mode 100644 index 0000000000..19a1d3bf55 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/delay.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/delayWhen.png b/docs_app/src/assets/images/marble-diagrams/delayWhen.png new file mode 100644 index 0000000000..2513fed9b0 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/delayWhen.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/dematerialize.png b/docs_app/src/assets/images/marble-diagrams/dematerialize.png new file mode 100644 index 0000000000..9e53d0ecd1 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/dematerialize.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/distinctUntilChanged.png b/docs_app/src/assets/images/marble-diagrams/distinctUntilChanged.png new file mode 100644 index 0000000000..cdf124656b Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/distinctUntilChanged.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/distinctUntilKeyChanged.png b/docs_app/src/assets/images/marble-diagrams/distinctUntilKeyChanged.png new file mode 100644 index 0000000000..ec8c481ec7 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/distinctUntilKeyChanged.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/elementAt.png b/docs_app/src/assets/images/marble-diagrams/elementAt.png new file mode 100644 index 0000000000..969544093f Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/elementAt.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/empty.png b/docs_app/src/assets/images/marble-diagrams/empty.png new file mode 100644 index 0000000000..ee035727ed Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/empty.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/endWith.png b/docs_app/src/assets/images/marble-diagrams/endWith.png new file mode 100644 index 0000000000..a1cd41465a Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/endWith.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/every.png b/docs_app/src/assets/images/marble-diagrams/every.png new file mode 100644 index 0000000000..f43e1ea009 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/every.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/exhaust.png b/docs_app/src/assets/images/marble-diagrams/exhaust.png new file mode 100644 index 0000000000..341d0d121c Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/exhaust.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/exhaustMap.png b/docs_app/src/assets/images/marble-diagrams/exhaustMap.png new file mode 100644 index 0000000000..4cdaa9fcae Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/exhaustMap.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/expand.png b/docs_app/src/assets/images/marble-diagrams/expand.png new file mode 100644 index 0000000000..46dce3a7f1 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/expand.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/filter.png b/docs_app/src/assets/images/marble-diagrams/filter.png new file mode 100644 index 0000000000..9f2755cb8a Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/filter.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/find.png b/docs_app/src/assets/images/marble-diagrams/find.png new file mode 100644 index 0000000000..4f91f1b2fe Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/find.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/findIndex.png b/docs_app/src/assets/images/marble-diagrams/findIndex.png new file mode 100644 index 0000000000..d405b9127f Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/findIndex.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/first.png b/docs_app/src/assets/images/marble-diagrams/first.png new file mode 100644 index 0000000000..6be24baa29 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/first.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/forkJoin.png b/docs_app/src/assets/images/marble-diagrams/forkJoin.png new file mode 100644 index 0000000000..5a4a6b39d8 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/forkJoin.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/from.png b/docs_app/src/assets/images/marble-diagrams/from.png new file mode 100644 index 0000000000..74a8473045 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/from.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/fromEvent.png b/docs_app/src/assets/images/marble-diagrams/fromEvent.png new file mode 100644 index 0000000000..499be0884a Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/fromEvent.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/fromEventPattern.png b/docs_app/src/assets/images/marble-diagrams/fromEventPattern.png new file mode 100644 index 0000000000..f80e97361a Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/fromEventPattern.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/generate.png b/docs_app/src/assets/images/marble-diagrams/generate.png new file mode 100644 index 0000000000..424c89e8ba Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/generate.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/groupBy.png b/docs_app/src/assets/images/marble-diagrams/groupBy.png new file mode 100644 index 0000000000..65ee282602 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/groupBy.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/ignoreElements.png b/docs_app/src/assets/images/marble-diagrams/ignoreElements.png new file mode 100644 index 0000000000..640f269763 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/ignoreElements.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/interval.png b/docs_app/src/assets/images/marble-diagrams/interval.png new file mode 100644 index 0000000000..6d62bc0c73 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/interval.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/isEmpty.png b/docs_app/src/assets/images/marble-diagrams/isEmpty.png new file mode 100644 index 0000000000..426f321aba Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/isEmpty.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/last.png b/docs_app/src/assets/images/marble-diagrams/last.png new file mode 100644 index 0000000000..38bf947f54 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/last.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/map.png b/docs_app/src/assets/images/marble-diagrams/map.png new file mode 100644 index 0000000000..74c3af550f Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/map.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/mapTo.png b/docs_app/src/assets/images/marble-diagrams/mapTo.png new file mode 100644 index 0000000000..216f5e723e Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/mapTo.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/materialize.png b/docs_app/src/assets/images/marble-diagrams/materialize.png new file mode 100644 index 0000000000..f3c41d01c3 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/materialize.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/max.png b/docs_app/src/assets/images/marble-diagrams/max.png new file mode 100644 index 0000000000..7f3498e822 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/max.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/merge.png b/docs_app/src/assets/images/marble-diagrams/merge.png new file mode 100644 index 0000000000..183fea0446 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/merge.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/mergeAll.png b/docs_app/src/assets/images/marble-diagrams/mergeAll.png new file mode 100644 index 0000000000..f4658f559f Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/mergeAll.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/mergeMap.png b/docs_app/src/assets/images/marble-diagrams/mergeMap.png new file mode 100644 index 0000000000..51f42cedc5 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/mergeMap.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/mergeMapTo.png b/docs_app/src/assets/images/marble-diagrams/mergeMapTo.png new file mode 100644 index 0000000000..01d3446062 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/mergeMapTo.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/min.png b/docs_app/src/assets/images/marble-diagrams/min.png new file mode 100644 index 0000000000..44464f782d Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/min.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/multicast.png b/docs_app/src/assets/images/marble-diagrams/multicast.png new file mode 100644 index 0000000000..678394d8dd Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/multicast.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/observeOn.png b/docs_app/src/assets/images/marble-diagrams/observeOn.png new file mode 100644 index 0000000000..77bcc432ff Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/observeOn.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/of.png b/docs_app/src/assets/images/marble-diagrams/of.png new file mode 100644 index 0000000000..ae7b9c9f46 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/of.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/onErrorResumeNext.png b/docs_app/src/assets/images/marble-diagrams/onErrorResumeNext.png new file mode 100644 index 0000000000..f7fca23f25 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/onErrorResumeNext.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/pairs.png b/docs_app/src/assets/images/marble-diagrams/pairs.png new file mode 100644 index 0000000000..cae1014044 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/pairs.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/pairwise.png b/docs_app/src/assets/images/marble-diagrams/pairwise.png new file mode 100644 index 0000000000..adb33e3b2e Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/pairwise.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/partition.png b/docs_app/src/assets/images/marble-diagrams/partition.png new file mode 100644 index 0000000000..d536190af6 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/partition.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/pluck.png b/docs_app/src/assets/images/marble-diagrams/pluck.png new file mode 100644 index 0000000000..2be8c15a41 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/pluck.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/publish.png b/docs_app/src/assets/images/marble-diagrams/publish.png new file mode 100644 index 0000000000..70f95f05b3 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/publish.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/publishBehavior.png b/docs_app/src/assets/images/marble-diagrams/publishBehavior.png new file mode 100644 index 0000000000..b1bad09e12 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/publishBehavior.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/publishLast.png b/docs_app/src/assets/images/marble-diagrams/publishLast.png new file mode 100644 index 0000000000..d974ad938b Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/publishLast.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/publishReplay.png b/docs_app/src/assets/images/marble-diagrams/publishReplay.png new file mode 100644 index 0000000000..a154e42d70 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/publishReplay.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/range.png b/docs_app/src/assets/images/marble-diagrams/range.png new file mode 100644 index 0000000000..19d0fc1407 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/range.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/reduce.png b/docs_app/src/assets/images/marble-diagrams/reduce.png new file mode 100644 index 0000000000..44f78f6bfd Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/reduce.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/refCount.png b/docs_app/src/assets/images/marble-diagrams/refCount.png new file mode 100644 index 0000000000..9791c315ee Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/refCount.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/repeat.png b/docs_app/src/assets/images/marble-diagrams/repeat.png new file mode 100644 index 0000000000..70533ad09d Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/repeat.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/repeatWhen.png b/docs_app/src/assets/images/marble-diagrams/repeatWhen.png new file mode 100644 index 0000000000..6689bd4d66 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/repeatWhen.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/retry.png b/docs_app/src/assets/images/marble-diagrams/retry.png new file mode 100644 index 0000000000..6938a35e29 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/retry.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/retryWhen.png b/docs_app/src/assets/images/marble-diagrams/retryWhen.png new file mode 100644 index 0000000000..7bf94d533d Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/retryWhen.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/sample.png b/docs_app/src/assets/images/marble-diagrams/sample.png new file mode 100644 index 0000000000..4f9f912c0b Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/sample.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/sampleTime.png b/docs_app/src/assets/images/marble-diagrams/sampleTime.png new file mode 100644 index 0000000000..8978216166 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/sampleTime.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/scan.png b/docs_app/src/assets/images/marble-diagrams/scan.png new file mode 100644 index 0000000000..88ce6c6e6a Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/scan.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/sequenceEqual.png b/docs_app/src/assets/images/marble-diagrams/sequenceEqual.png new file mode 100644 index 0000000000..adc1fcee7a Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/sequenceEqual.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/share.png b/docs_app/src/assets/images/marble-diagrams/share.png new file mode 100644 index 0000000000..1d83927718 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/share.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/single.png b/docs_app/src/assets/images/marble-diagrams/single.png new file mode 100644 index 0000000000..de36ed6a3a Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/single.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/skip.png b/docs_app/src/assets/images/marble-diagrams/skip.png new file mode 100644 index 0000000000..af430a288f Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/skip.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/skipLast.png b/docs_app/src/assets/images/marble-diagrams/skipLast.png new file mode 100644 index 0000000000..7735871524 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/skipLast.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/skipUntil.png b/docs_app/src/assets/images/marble-diagrams/skipUntil.png new file mode 100644 index 0000000000..a0146c9711 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/skipUntil.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/skipWhile.png b/docs_app/src/assets/images/marble-diagrams/skipWhile.png new file mode 100644 index 0000000000..286a13d017 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/skipWhile.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/startWith.png b/docs_app/src/assets/images/marble-diagrams/startWith.png new file mode 100644 index 0000000000..2ca6144bbb Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/startWith.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/subscribeOn.png b/docs_app/src/assets/images/marble-diagrams/subscribeOn.png new file mode 100644 index 0000000000..2946fd34b7 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/subscribeOn.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/switchAll.png b/docs_app/src/assets/images/marble-diagrams/switchAll.png new file mode 100644 index 0000000000..964893713d Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/switchAll.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/switchMap.png b/docs_app/src/assets/images/marble-diagrams/switchMap.png new file mode 100644 index 0000000000..b313cc4d80 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/switchMap.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/switchMapTo.png b/docs_app/src/assets/images/marble-diagrams/switchMapTo.png new file mode 100644 index 0000000000..8f7f442177 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/switchMapTo.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/take.png b/docs_app/src/assets/images/marble-diagrams/take.png new file mode 100644 index 0000000000..4cc88ea96f Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/take.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/takeLast.png b/docs_app/src/assets/images/marble-diagrams/takeLast.png new file mode 100644 index 0000000000..4f52c3ec4f Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/takeLast.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/takeUntil.png b/docs_app/src/assets/images/marble-diagrams/takeUntil.png new file mode 100644 index 0000000000..f69241f123 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/takeUntil.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/takeWhile.png b/docs_app/src/assets/images/marble-diagrams/takeWhile.png new file mode 100644 index 0000000000..5ab21ef300 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/takeWhile.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/tap.png b/docs_app/src/assets/images/marble-diagrams/tap.png new file mode 100644 index 0000000000..72739499bc Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/tap.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/throttle.png b/docs_app/src/assets/images/marble-diagrams/throttle.png new file mode 100644 index 0000000000..e778e6404a Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/throttle.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/throttleTime.png b/docs_app/src/assets/images/marble-diagrams/throttleTime.png new file mode 100644 index 0000000000..5c0cc43ab8 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/throttleTime.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/throw.png b/docs_app/src/assets/images/marble-diagrams/throw.png new file mode 100644 index 0000000000..d687a5bb0e Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/throw.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/throwIfEmpty.png b/docs_app/src/assets/images/marble-diagrams/throwIfEmpty.png new file mode 100644 index 0000000000..062955df4d Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/throwIfEmpty.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/timeInterval.png b/docs_app/src/assets/images/marble-diagrams/timeInterval.png new file mode 100644 index 0000000000..1f8ae1c817 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/timeInterval.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/timeout.png b/docs_app/src/assets/images/marble-diagrams/timeout.png new file mode 100644 index 0000000000..9b79804e71 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/timeout.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/timeoutWith.png b/docs_app/src/assets/images/marble-diagrams/timeoutWith.png new file mode 100644 index 0000000000..a15cd5efbd Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/timeoutWith.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/timer.png b/docs_app/src/assets/images/marble-diagrams/timer.png new file mode 100644 index 0000000000..0870ae2763 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/timer.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/timestamp.png b/docs_app/src/assets/images/marble-diagrams/timestamp.png new file mode 100644 index 0000000000..3f59e981d3 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/timestamp.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/toArray.png b/docs_app/src/assets/images/marble-diagrams/toArray.png new file mode 100644 index 0000000000..21bbbc7672 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/toArray.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/window.png b/docs_app/src/assets/images/marble-diagrams/window.png new file mode 100644 index 0000000000..2e22e4b9ad Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/window.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/windowCount.png b/docs_app/src/assets/images/marble-diagrams/windowCount.png new file mode 100644 index 0000000000..b0a259cab9 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/windowCount.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/windowTime.png b/docs_app/src/assets/images/marble-diagrams/windowTime.png new file mode 100644 index 0000000000..402e4e6b84 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/windowTime.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/windowToggle.png b/docs_app/src/assets/images/marble-diagrams/windowToggle.png new file mode 100644 index 0000000000..fe14fa9b3b Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/windowToggle.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/windowWhen.png b/docs_app/src/assets/images/marble-diagrams/windowWhen.png new file mode 100644 index 0000000000..70c9404da6 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/windowWhen.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/withLatestFrom.png b/docs_app/src/assets/images/marble-diagrams/withLatestFrom.png new file mode 100644 index 0000000000..9c37244729 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/withLatestFrom.png differ diff --git a/docs_app/src/assets/images/marble-diagrams/zipAll.png b/docs_app/src/assets/images/marble-diagrams/zipAll.png new file mode 100644 index 0000000000..708d8783b9 Binary files /dev/null and b/docs_app/src/assets/images/marble-diagrams/zipAll.png differ diff --git a/docs_app/src/assets/js/devtools-welcome.js b/docs_app/src/assets/js/devtools-welcome.js index 38482e750f..fffd533ba2 100644 --- a/docs_app/src/assets/js/devtools-welcome.js +++ b/docs_app/src/assets/js/devtools-welcome.js @@ -4,11 +4,9 @@ var welcomeText = ( '| |_) |\\ \\/ / | \\___ \\ \n' + '| _ < > < |_| |___) | \n' + '|_| \\_\\/_/\\_\\___/|____/ \n' + - '\nOpen http://stackblitz.com and try this code to get\n' + '\nstarted experimenting with RxJS:\n' + - '\nimport { interval } from "rxjs"\n' + - '\nimport { take } from "rxjs/operators"\n' + - '\nconst subscription = interval(500).pipe(take(4)).subscribe(console.log)\n' + '\ntype this into the console:\n' + + '\nrxjs.interval(500).pipe(rxjs.operators.take(4)).subscribe(console.log)\n' ); if (console.info) { console.info(welcomeText); diff --git a/docs_app/src/environments/environment.archive.ts b/docs_app/src/environments/environment.archive.ts index b1f01bce04..f340b56377 100644 --- a/docs_app/src/environments/environment.archive.ts +++ b/docs_app/src/environments/environment.archive.ts @@ -1,6 +1,6 @@ // This is for archived sites, which are hosted at https://vX.angular.io, where X is the major Angular version. export const environment = { - gaId: 'UA-8594346-28', // Production id (since it is linked from the main site) + gaId: 'UA-36380079-2', // Production id (since it is linked from the main site) production: true, mode: 'archive' }; diff --git a/docs_app/src/environments/environment.next.ts b/docs_app/src/environments/environment.next.ts index 7a921ba3d5..9c2107f044 100644 --- a/docs_app/src/environments/environment.next.ts +++ b/docs_app/src/environments/environment.next.ts @@ -1,6 +1,6 @@ // This is for the staging site, which is hosted at https://next.angular.io (and https://aio-staging.firebaseapp.org) export const environment = { - gaId: 'UA-8594346-28', // Production id (since it is linked from the main site) + gaId: 'UA-36380079-2', // Production id (since it is linked from the main site) production: true, mode: 'next' }; diff --git a/docs_app/src/environments/environment.stable.ts b/docs_app/src/environments/environment.stable.ts index 9b3433e1aa..6087cf13cf 100644 --- a/docs_app/src/environments/environment.stable.ts +++ b/docs_app/src/environments/environment.stable.ts @@ -1,6 +1,6 @@ // This is for the production site, which is hosted at https://angular.io export const environment = { - gaId: 'UA-8594346-28', // Production id + gaId: 'UA-36380079-2', // Production id production: true, mode: 'stable' }; diff --git a/docs_app/src/environments/environment.ts b/docs_app/src/environments/environment.ts index 77d62f0968..75dba29396 100644 --- a/docs_app/src/environments/environment.ts +++ b/docs_app/src/environments/environment.ts @@ -13,7 +13,7 @@ import 'core-js/es7/reflect'; export const environment = { - gaId: 'UA-8594346-26', // Development id + gaId: 'UA-36380079-2', // Development id production: false, mode: 'stable' }; diff --git a/docs_app/src/index.html b/docs_app/src/index.html index e2cab88cdd..e2c7db9758 100644 --- a/docs_app/src/index.html +++ b/docs_app/src/index.html @@ -151,6 +151,7 @@

This website requires JavaScript.

+ diff --git a/docs_app/src/styles/1-layouts/_marketing-layout.scss b/docs_app/src/styles/1-layouts/_marketing-layout.scss index c2762fba91..c6014daa4d 100644 --- a/docs_app/src/styles/1-layouts/_marketing-layout.scss +++ b/docs_app/src/styles/1-layouts/_marketing-layout.scss @@ -276,12 +276,7 @@ section#intro { } aio-shell { - &.page-resources, &.page-events, &.page-features, &.page-presskit, &.page-contribute { - section { - padding: 0rem 0rem 3rem; - } - } - + &.page-home { section { padding: 0; diff --git a/docs_app/src/styles/2-modules/_api-list.scss b/docs_app/src/styles/2-modules/_api-list.scss index 383c2e906e..bca2dd52f9 100644 --- a/docs_app/src/styles/2-modules/_api-list.scss +++ b/docs_app/src/styles/2-modules/_api-list.scss @@ -199,6 +199,16 @@ aio-api-list { color: $blue-500; } } + + .stability { + &.deprecated { + text-decoration: line-through; + } + + &.experimental { + font-style: italic; + } + } } } diff --git a/docs_app/tools/transforms/angular-api-package/index.js b/docs_app/tools/transforms/angular-api-package/index.js index fafc7aed4d..75a7762657 100644 --- a/docs_app/tools/transforms/angular-api-package/index.js +++ b/docs_app/tools/transforms/angular-api-package/index.js @@ -70,6 +70,7 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage]) 'index.ts', 'operators/index.ts', 'ajax/index.ts', + 'fetch/index.ts', 'webSocket/index.ts', 'testing/index.ts' ]; diff --git a/docs_app/tools/transforms/angular-api-package/post-processors/embedMarbleDiagrams.js b/docs_app/tools/transforms/angular-api-package/post-processors/embedMarbleDiagrams.js index 987d65b6c8..7309502f33 100644 --- a/docs_app/tools/transforms/angular-api-package/post-processors/embedMarbleDiagrams.js +++ b/docs_app/tools/transforms/angular-api-package/post-processors/embedMarbleDiagrams.js @@ -20,7 +20,6 @@ module.exports = function embedMarbleDiagramsPostProcessor(log) { const props = node.properties; const src = props.src; const expectedImgPath = `${service.marbleImagesPath}/${src}`; - if (fs.existsSync(expectedImgPath)) { const operator = path.basename(src, path.extname(src)); const filename = path.basename(expectedImgPath); diff --git a/docs_app/tools/transforms/config.js b/docs_app/tools/transforms/config.js index 2dc73e7475..2c1fa9305b 100644 --- a/docs_app/tools/transforms/config.js +++ b/docs_app/tools/transforms/config.js @@ -10,9 +10,9 @@ const SRC_PATH = resolve(AIO_PATH, 'src'); const OUTPUT_PATH = resolve(SRC_PATH, 'generated'); const DOCS_OUTPUT_PATH = resolve(OUTPUT_PATH, 'docs'); const API_SOURCE_PATH = resolve(PROJECT_ROOT, 'src'); -const MARBLE_IMAGES_PATH = resolve(PROJECT_ROOT, 'tmp/docs/img'); -const MARBLE_IMAGES_WEB_PATH = 'generated/images/marbles'; -const MARBLE_IMAGES_OUTPUT_PATH = resolve(OUTPUT_PATH, 'images/marbles'); +const MARBLE_IMAGES_PATH = resolve(SRC_PATH, 'assets/images/marble-diagrams'); +const MARBLE_IMAGES_WEB_PATH = 'assets/images/marble-diagrams'; +const MARBLE_IMAGES_OUTPUT_PATH = resolve('assets/images/marble-diagrams'); function requireFolder(dirname, folderPath) { const absolutePath = resolve(dirname, folderPath); diff --git a/integration/systemjs/systemjs-compatibility-spec.js b/integration/systemjs/systemjs-compatibility-spec.js index 4c672575a1..cb1713c2c5 100644 --- a/integration/systemjs/systemjs-compatibility-spec.js +++ b/integration/systemjs/systemjs-compatibility-spec.js @@ -6,6 +6,7 @@ System.config({ packages: { 'rxjs': {main: 'index.js', defaultExtension: 'js' }, 'rxjs/ajax': {main: 'index.js', defaultExtension: 'js' }, + 'rxjs/fetch': {main: 'index.js', defaultExtension: 'js' }, 'rxjs/operators': {main: 'index.js', defaultExtension: 'js' }, 'rxjs/testing': {main: 'index.js', defaultExtension: 'js' }, 'rxjs/webSocket': {main: 'index.js', defaultExtension: 'js' } @@ -15,6 +16,7 @@ System.config({ Promise.all([ System.import('rxjs'), System.import('rxjs/ajax'), + System.import('rxjs/fetch'), System.import('rxjs/operators'), System.import('rxjs/testing'), System.import('rxjs/webSocket'), diff --git a/package-lock.json b/package-lock.json index e57ed5bb73..3e26e6876c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@reactivex/rxjs", - "version": "6.3.3", + "version": "6.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -165,6 +165,12 @@ "integrity": "sha512-DvC7bzO5797bkApgukxouHmkOdYN2D0yL5olw0RncDpXUa6n39qTVsUi/5g2QJjPgl8qn4zh+4h0sofNoWGLRg==", "dev": true }, + "@types/parsimmon": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@types/parsimmon/-/parsimmon-1.10.0.tgz", + "integrity": "sha512-bsTIJFVQv7jnvNiC42ld2pQW2KRI+pAG243L+iATvqzy3X6+NH1obz2itRKDZZ8VVhN3wjwYax/VBGCcXzgTqQ==", + "dev": true + }, "@types/sinon": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-4.1.3.tgz", @@ -2953,6 +2959,16 @@ "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=", "dev": true }, + "definitelytyped-header-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/definitelytyped-header-parser/-/definitelytyped-header-parser-1.1.0.tgz", + "integrity": "sha512-goGRhRUJVQhXc28OjwSCoAoutl/dcFUJXwEv3DYdtxGFCXWK+YFS/MrFfsAnHG0ZYPKBcniXWRUlr3UHGKaTHw==", + "dev": true, + "requires": { + "@types/parsimmon": "^1.3.0", + "parsimmon": "^1.2.0" + } + }, "del": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", @@ -3188,6 +3204,566 @@ "domelementtype": "1" } }, + "download-file-sync": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/download-file-sync/-/download-file-sync-1.0.4.tgz", + "integrity": "sha1-0+PFQ/g29BA5RVuQNMcuNVsDYBk=", + "dev": true + }, + "dts-critic": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/dts-critic/-/dts-critic-1.0.5.tgz", + "integrity": "sha512-//zIvVV+sAp9Gmke7Ao+e3dGFqzX5f8xvu3CS2uGyxt98+rLHaEyAr0mhdsnQGhSHooD4A/8rQGtmpxqo8Qa+g==", + "dev": true, + "requires": { + "definitelytyped-header-parser": "^1.0.1", + "download-file-sync": "^1.0.4", + "yargs": "^12.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "dtslint": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/dtslint/-/dtslint-0.6.1.tgz", + "integrity": "sha512-0YWn8o5LxjuEtr3G7CoDJev0fv0qwhm4ThOsnitWYWpBf/EBUs/DK7IenZkB2/lozJ7W+RVO5fJKJ5wwbE+Y3g==", + "dev": true, + "requires": { + "definitelytyped-header-parser": "^1.0.1", + "dts-critic": "^1.0.5", + "fs-extra": "^6.0.1", + "request": "^2.88.0", + "strip-json-comments": "^2.0.1", + "tslint": "5.14.0", + "typescript": "^3.5.0-dev.20190412" + }, + "dependencies": { + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-extra": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", + "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "mime-db": { + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", + "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", + "dev": true + }, + "mime-types": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", + "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "dev": true, + "requires": { + "mime-db": "~1.38.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tslint": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.14.0.tgz", + "integrity": "sha512-IUla/ieHVnB8Le7LdQFRGlVJid2T/gaJe5VkjzRVSRR6pA2ODYrnfR1hmxi+5+au9l50jBwpbBL34txgv4NnTQ==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.29.0" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "typescript": { + "version": "3.5.0-dev.20190412", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.0-dev.20190412.tgz", + "integrity": "sha512-ggfmPI3Jddh42pOzeAqQ5EaPIjFPJNlmFzKOYVj/Rybz6o4QCl5o0fwfitAPatFhY5k6I7E5D90zURXhr1W4rQ==", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + } + } + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", @@ -4407,7 +4983,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -4809,7 +5386,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -4864,6 +5442,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -4907,12 +5486,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -8323,6 +8904,15 @@ "tmpl": "1.0.x" } }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -8828,6 +9418,12 @@ "integrity": "sha1-a+lPysqNd63gqWcNxGCRTJRHJEQ=", "dev": true }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "nise": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/nise/-/nise-1.2.5.tgz", @@ -11145,12 +11741,24 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, "p-limit": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", @@ -11262,8 +11870,7 @@ "version": "1.12.0", "resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.12.0.tgz", "integrity": "sha512-uC/BjuSfb4jfaWajKCp1mVncXXq+V1twbcYChbTxN3GM7fn+8XoHwUdvUz+PTaFtDSCRQxU8+Rnh+iMhAkVwdw==", - "dev": true, - "optional": true + "dev": true }, "pascalcase": { "version": "0.1.1", @@ -11835,6 +12442,22 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -14308,6 +14931,23 @@ "integrity": "sha1-RY8Xgg03gg3GDiC4bZQ5GwASMVg=", "dev": true }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", diff --git a/package.json b/package.json index e74e01c8e8..970b739bf1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@reactivex/rxjs", - "version": "6.4.0", + "version": "6.5.0", "description": "Reactive Extensions for modern JavaScript", "main": "index.js", "sideEffects": false, @@ -88,11 +88,11 @@ "lint_spec": "tslint -c tslint.json \"spec/**/*.ts\"", "lint_src": "tslint -c tslint.json \"src/**/*.ts\"", "lint": "npm-run-all --parallel lint_*", - "dtslint": "npm run build_types && npx dtslint spec-dtslint", + "dtslint": "npm run build_types && node ./spec-dtslint/script.js", "perf:protractor": "echo \"Protractor is not working currently\" && exit -1 && protractor protractor.conf.js", "perf:micro": "node ./perf/micro/index.js", "prepublish": "shx rm -rf ./typings && npm run build_all", - "postpublish": "npm run tests2png && ./docs_app/scripts/publish-docs.sh", + "postpublish": "./docs_app/scripts/publish-docs.sh", "publish_docs": "./publish_docs.sh", "test": "cross-env TS_NODE_PROJECT=spec/tsconfig.json mocha --opts spec/support/default.opts \"spec/**/*-spec.ts\"", "test_no_cache": "cross-env TS_NODE_PROJECT=spec/tsconfig.json TS_NODE_CACHE=false mocha --opts spec/support/default.opts \"spec/**/*-spec.ts\"", @@ -196,6 +196,7 @@ "danger": "1.1.0", "dependency-cruiser": "2.13.0", "doctoc": "1.3.0", + "dtslint": "0.6.1", "escape-string-regexp": "1.0.5", "esdoc": "0.4.7", "eslint": "4.17.0", diff --git a/resources/CI-CD/README.md b/resources/CI-CD/README.md new file mode 100644 index 0000000000..1d423e9739 --- /dev/null +++ b/resources/CI-CD/README.md @@ -0,0 +1,2 @@ +This folder contains all resources, docs licenses or other information +related to the corporate identity and corporate design of this project. diff --git a/resources/CI-CD/logo/svg/RxJs_Logo_Basic.svg b/resources/CI-CD/logo/svg/RxJs_Logo_Basic.svg new file mode 100644 index 0000000000..3e4f5ad4e5 --- /dev/null +++ b/resources/CI-CD/logo/svg/RxJs_Logo_Basic.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/CI-CD/logo/svg/RxJs_Logo_Basic_Outline.svg b/resources/CI-CD/logo/svg/RxJs_Logo_Basic_Outline.svg new file mode 100644 index 0000000000..302578e61c --- /dev/null +++ b/resources/CI-CD/logo/svg/RxJs_Logo_Basic_Outline.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/CI-CD/logo/svg/RxJs_Logo_Black.svg b/resources/CI-CD/logo/svg/RxJs_Logo_Black.svg new file mode 100644 index 0000000000..0b934c4714 --- /dev/null +++ b/resources/CI-CD/logo/svg/RxJs_Logo_Black.svg @@ -0,0 +1,36 @@ + + + + + + + + diff --git a/resources/CI-CD/logo/svg/RxJs_Logo_Black_Outline.svg b/resources/CI-CD/logo/svg/RxJs_Logo_Black_Outline.svg new file mode 100644 index 0000000000..a89962d0ed --- /dev/null +++ b/resources/CI-CD/logo/svg/RxJs_Logo_Black_Outline.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + diff --git a/spec-dtslint/observables/forkJoin-spec.ts b/spec-dtslint/observables/forkJoin-spec.ts index 4d8936838b..8ee44a11e8 100644 --- a/spec-dtslint/observables/forkJoin-spec.ts +++ b/spec-dtslint/observables/forkJoin-spec.ts @@ -1,94 +1,104 @@ import { of, forkJoin } from 'rxjs'; -it('should infer correctly with 1 parameter', () => { - const a = of(1, 2, 3); - const res = forkJoin(a); // $ExpectType Observable -}); +describe('deprecated rest args', () => { + it('should infer correctly with 1 parameter', () => { + const a = of(1, 2, 3); + const res = forkJoin(a); // $ExpectType Observable<[number]> + }); -it('should infer correctly with 2 parameters', () => { - const a = of(1, 2, 3); - const b = of('a', 'b', 'c'); - const res = forkJoin(a, b); // $ExpectType Observable<[number, string]> -}); + it('should infer correctly with 2 parameters', () => { + const a = of(1, 2, 3); + const b = of('a', 'b', 'c'); + const res = forkJoin(a, b); // $ExpectType Observable<[number, string]> + }); -it('should infer correctly with 3 parameters', () => { - const a = of(1, 2, 3); - const b = of('a', 'b', 'c'); - const c = of(1, 2, 3); - const res = forkJoin(a, b, c); // $ExpectType Observable<[number, string, number]> -}); + it('should infer correctly with 3 parameters', () => { + const a = of(1, 2, 3); + const b = of('a', 'b', 'c'); + const c = of(1, 2, 3); + const res = forkJoin(a, b, c); // $ExpectType Observable<[number, string, number]> + }); -it('should infer correctly with 4 parameters', () => { - const a = of(1, 2, 3); - const b = of('a', 'b', 'c'); - const c = of(1, 2, 3); - const d = of(1, 2, 3); - const res = forkJoin(a, b, c, d); // $ExpectType Observable<[number, string, number, number]> -}); + it('should infer correctly with 4 parameters', () => { + const a = of(1, 2, 3); + const b = of('a', 'b', 'c'); + const c = of(1, 2, 3); + const d = of(1, 2, 3); + const res = forkJoin(a, b, c, d); // $ExpectType Observable<[number, string, number, number]> + }); -it('should infer correctly with 5 parameters', () => { - const a = of(1, 2, 3); - const b = of('a', 'b', 'c'); - const c = of(1, 2, 3); - const d = of(1, 2, 3); - const e = of(1, 2, 3); - const res = forkJoin(a, b, c, d, e); // $ExpectType Observable<[number, string, number, number, number]> + it('should infer correctly with 5 parameters', () => { + const a = of(1, 2, 3); + const b = of('a', 'b', 'c'); + const c = of(1, 2, 3); + const d = of(1, 2, 3); + const e = of(1, 2, 3); + const res = forkJoin(a, b, c, d, e); // $ExpectType Observable<[number, string, number, number, number]> + }); + + it('should infer correctly with 6 parameters', () => { + const a = of(1, 2, 3); + const b = of('a', 'b', 'c'); + const c = of(1, 2, 3); + const d = of(1, 2, 3); + const e = of(1, 2, 3); + const f = of(1, 2, 3); + const res = forkJoin(a, b, c, d, e, f); // $ExpectType Observable<[number, string, number, number, number, number]> + }); }); -it('should infer correctly with 6 parameters', () => { +it('should infer of type any for more than 6 parameters', () => { const a = of(1, 2, 3); const b = of('a', 'b', 'c'); const c = of(1, 2, 3); const d = of(1, 2, 3); const e = of(1, 2, 3); const f = of(1, 2, 3); - const res = forkJoin(a, b, c, d, e, f); // $ExpectType Observable<[number, string, number, number, number, number]> + const g = of(1, 2, 3); + const res = forkJoin(a, b, c, d, e, f, g); // $ExpectType Observable }); -// TODO(benlesh): this needs to be fixed as well -// it('should infer of type any for more than 6 parameters', () => { -// const a = of(1, 2, 3); -// const b = of('a', 'b', 'c'); -// const c = of(1, 2, 3); -// const d = of(1, 2, 3); -// const e = of(1, 2, 3); -// const f = of(1, 2, 3); -// const g = of(1, 2, 3); -// const res = forkJoin(a, b, c, d, e, f, g); // $ExpectType Observable<{}> -// }); - -it('should infer correctly for array of 1 observable', () => { - const a = [of(1, 2, 3)]; - const res = forkJoin(a); // $ExpectType Observable +describe('forkJoin({})', () => { + it('should properly type empty objects', () => { + const res = forkJoin({}); // $ExpectType Observable + }); + + it('should work for the simple case', () => { + const res = forkJoin({ foo: of(1), bar: of('two'), baz: of(false) }); // $ExpectType Observable<{ foo: number; bar: string; baz: boolean; }> + }); }); -// TODO(benlesh): We need to fix forkJoin so these pass -// it('should infer correctly for array of 2 observables', () => { -// const a = [of(1, 2, 3), of('a', 'b', 'c')]; -// const res = forkJoin(a); // $ExpectType Observable<[number, string]> -// }); - -// it('should infer correctly for array of 3 observables', () => { -// const a = [of(1, 2, 3), of('a', 'b', 'c'), of(true, true, false)]; -// const res = forkJoin(a); // $ExpectType Observable<[number, string, boolean]> -// }); - -// it('should infer correctly for array of 4 observables', () => { -// const a = [of(1, 2, 3), of('a', 'b', 'c'), of(1, 2, 3), of(1, 2, 3)]; -// const res = forkJoin(a); // $ExpectType Observable<[number, string, number, number]> -// }); - -// it('should infer correctly for array of 5 observables', () => { -// const a = [of(1, 2, 3), of('a', 'b', 'c'), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3)]; -// const res = forkJoin(a); // $ExpectType Observable<[number, string, number, number, number]> -// }); - -// it('should infer correctly for array of 6 observables', () => { -// const a = [of(1, 2, 3), of('a', 'b', 'c'), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3)]; -// const res = forkJoin(a); // $ExpectType Observable<[number, string, number, number, number, number]> -// }); - -// it('should force user cast for array of 6+ observables', () => { -// const a = [of(1, 2, 3), of('a', 'b', 'c'), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3)]; -// const res = forkJoin(a); // $ExpectType Observable<{}> -// }); +describe('forkJoin([])', () => { + // TODO(benlesh): Uncomment for TS 3.0 + // it('should properly type empty arrays', () => { + // const res = forkJoin([]); // $ExpectType Observable + // }); + + it('should infer correctly for array of 1 observable', () => { + const res = forkJoin([of(1, 2, 3)]); // $ExpectType Observable<[number]> + }); + + it('should infer correctly for array of 2 observables', () => { + const res = forkJoin([of(1, 2, 3), of('a', 'b', 'c')]); // $ExpectType Observable<[number, string]> + }); + + it('should infer correctly for array of 3 observables', () => { + const res = forkJoin([of(1, 2, 3), of('a', 'b', 'c'), of(true, true, false)]); // $ExpectType Observable<[number, string, boolean]> + }); + + it('should infer correctly for array of 4 observables', () => { + const res = forkJoin([of(1, 2, 3), of('a', 'b', 'c'), of(1, 2, 3), of(1, 2, 3)]); // $ExpectType Observable<[number, string, number, number]> + }); + + it('should infer correctly for array of 5 observables', () => { + const res = forkJoin([of(1, 2, 3), of('a', 'b', 'c'), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3)]); // $ExpectType Observable<[number, string, number, number, number]> + }); + + it('should infer correctly for array of 6 observables', () => { + const res = forkJoin([of(1, 2, 3), of('a', 'b', 'c'), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3)]); // $ExpectType Observable<[number, string, number, number, number, number]> + }); + + it('should force user cast for array of 6+ observables', () => { + const res = forkJoin([of(1, 2, 3), of('a', 'b', 'c'), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3), of(1, 2, 3)]); // $ExpectType Observable<(string | number)[]> + }); +}); diff --git a/spec-dtslint/observables/from-spec.ts b/spec-dtslint/observables/from-spec.ts index 44b9258c9b..acc406b55e 100644 --- a/spec-dtslint/observables/from-spec.ts +++ b/spec-dtslint/observables/from-spec.ts @@ -36,13 +36,21 @@ it('should accept an array of Observables', () => { const o = from([of(1), of(2), of(3)]); // $ExpectType Observable> }); -it('should accept an array of Inputs', () => { - const iterable = (function*() { - yield 42; - }()); +// TODO(benlesh): enable this test, once the issue is resolved upstream (https://github.com/Microsoft/dtslint/issues/191) - const o = from([of(1), ['test'], iterable]); // $ExpectType Observable | Observable | string[]> -}); +// NOTE: It appears to be working, it's just that dtslint sometimes says it wants +// Observable | Observable | string[]> +// and if you switch it to that, it wants +// Observable | IterableIterator | string[]> +// and vica versa. + +// it('should accept an array of Inputs', () => { +// const iterable = (function*() { +// yield 42; +// }()); + +// const o = from([of(1), ['test'], iterable]); // $__TODO__ExpectType Observable | Observable | string[]> +// }); it('should support scheduler', () => { const a = from([1, 2, 3], animationFrameScheduler); // $ExpectType Observable diff --git a/spec-dtslint/observables/partition-spec.ts b/spec-dtslint/observables/partition-spec.ts new file mode 100644 index 0000000000..1f3af18656 --- /dev/null +++ b/spec-dtslint/observables/partition-spec.ts @@ -0,0 +1,20 @@ +import { of, partition } from 'rxjs'; + +it('should infer correctly', () => { + const o = partition(of('a', 'b', 'c'), (value, index) => true); // $ExpectType [Observable, Observable] + const p = partition(of('a', 'b', 'c'), () => true); // $ExpectType [Observable, Observable] +}); + +it('should accept a thisArg parameter', () => { + const o = partition(of('a', 'b', 'c'), () => true, 5); // $ExpectType [Observable, Observable] +}); + +it('should enforce predicate', () => { + const o = partition(of('a', 'b', 'c')); // $ExpectError +}); + +it('should enforce predicate types', () => { + const o = partition(of('a', 'b', 'c'), 'nope'); // $ExpectError + const p = partition(of('a', 'b', 'c'), (value: number) => true); // $ExpectError + const q = partition(of('a', 'b', 'c'), (value, index: string) => true); // $ExpectError +}); diff --git a/spec-dtslint/observables/race-spec.ts b/spec-dtslint/observables/race-spec.ts index 805d8a6137..e31f1465f2 100644 --- a/spec-dtslint/observables/race-spec.ts +++ b/spec-dtslint/observables/race-spec.ts @@ -5,65 +5,116 @@ it('should infer correctly with 1 parameter', () => { const o = race(a); // $ExpectType Observable }); -it('should infer correctly with multiple parameters of the same type', () => { - const a = of(1); - const b = of(2); - const o = race(a, b); // $ExpectType Observable -}); +describe('race(a, b, c)', () => { + it('should infer correctly with multiple parameters of the same type', () => { + const a = of(1); + const b = of(2); + const o = race(a, b); // $ExpectType Observable + }); -it('should support 2 parameters with different types', () => { - const a = of(1); - const b = of('a'); - const o = race(a, b); // $ExpectType Observable | Observable -}); + it('should support 2 parameters with different types', () => { + const a = of(1); + const b = of('a'); + const o = race(a, b); // $ExpectType Observable + }); -it('should support 3 parameters with different types', () => { - const a = of(1); - const b = of('a'); - const c = of(true); - const o = race(a, b, c); // $ExpectType Observable | Observable | Observable -}); + it('should support 3 parameters with different types', () => { + const a = of(1); + const b = of('a'); + const c = of(true); + const o = race(a, b, c); // $ExpectType Observable + }); -it('should support 4 parameters with different types', () => { - const a = of(1); - const b = of('a'); - const c = of(true); - const d = of([1, 2, 3]); - const o = race(a, b, c, d); // $ExpectType Observable | Observable | Observable | Observable -}); + it('should support 4 parameters with different types', () => { + const a = of(1); + const b = of('a'); + const c = of(true); + const d = of([1, 2, 3]); + const o = race(a, b, c, d); // $ExpectType Observable + }); -it('should support 5 parameters with different types', () => { - const a = of(1); - const b = of('a'); - const c = of(true); - const d = of([1, 2, 3]); - const e = of(['blah']); - const o = race(a, b, c, d, e); // $ExpectType Observable | Observable | Observable | Observable | Observable -}); + it('should support 5 parameters with different types', () => { + const a = of(1); + const b = of('a'); + const c = of(true); + const d = of([1, 2, 3]); + const e = of(['blah']); + const o = race(a, b, c, d, e); // $ExpectType Observable + }); -it('should support 6 or more parameters of the same type', () => { - const a = of(1); - const o = race(a, a, a, a, a, a, a, a, a, a, a, a, a, a); // $ExpectType Observable + it('should support 6 or more parameters of the same type', () => { + const a = of(1); + const o = race(a, a, a, a, a, a, a, a, a, a, a, a, a, a); // $ExpectType Observable + }); + + it('should return {} for 6 or more arguments of different types', () => { + const a = of(1); + const b = of('a'); + const c = of(true); + const d = of([1, 2, 3]); + const e = of(['blah']); + const f = of({ foo: 'bar' }); + const o = race(a, b, c, d, e, f); // $ExpectType Observable<{}> + }); }); -it('should return {} for 6 or more arguments of different types', () => { - const a = of(1); - const b = of('a'); - const c = of(true); - const d = of([1, 2, 3]); - const e = of(['blah']); - const f = of({ foo: 'bar' }); - const o = race(a, b, c, d, e, f); // $ExpectType Observable<{}> +describe('race([a, b, c])', () => { + it('should infer correctly with multiple parameters of the same type', () => { + const a = of(1); + const b = of(2); + const o = race([a, b]); // $ExpectType Observable + }); + + it('should support 2 parameters with different types', () => { + const a = of(1); + const b = of('a'); + const o = race([a, b]); // $ExpectType Observable + }); + + it('should support 3 parameters with different types', () => { + const a = of(1); + const b = of('a'); + const c = of(true); + const o = race([a, b, c]); // $ExpectType Observable + }); + + it('should support 4 parameters with different types', () => { + const a = of(1); + const b = of('a'); + const c = of(true); + const d = of([1, 2, 3]); + const o = race([a, b, c, d]); // $ExpectType Observable + }); + + it('should support 5 parameters with different types', () => { + const a = of(1); + const b = of('a'); + const c = of(true); + const d = of([1, 2, 3]); + const e = of(['blah']); + const o = race([a, b, c, d, e]); // $ExpectType Observable + }); + + it('should support 6 or more parameters of the same type', () => { + const a = of(1); + const o = race([a, a, a, a, a, a, a, a, a, a, a, a, a, a]); // $ExpectType Observable + }); + + it('should return {} for 6 or more arguments of different types', () => { + const a = of(1); + const b = of('a'); + const c = of(true); + const d = of([1, 2, 3]); + const e = of(['blah']); + const f = of({ foo: 'bar' }); + const o = race([a, b, c, d, e, f]); // $ExpectType Observable<{}> + }); }); -it('should handle an array of observables', () => { - const a = of(1); - const b = of(2); - const o = race([a, b]); // $ExpectType Observable +it('should race observable inputs', () => { + const o = race(of(1), Promise.resolve('foo'), [true, false]); // $ExpectType Observable }); -it('should return {} for array of observables of different types', () => { - const a = of(1); - const b = of('test'); - const o = race([a, b]); // $ExpectType Observable<{}> +it('should race an array observable inputs', () => { + const o = race([of(1), Promise.resolve('foo'), [true, false]]); // $ExpectType Observable }); diff --git a/spec-dtslint/operators/partition-spec.ts b/spec-dtslint/operators/partition-spec.ts deleted file mode 100644 index cec8b9c71b..0000000000 --- a/spec-dtslint/operators/partition-spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as Rx from 'rxjs/Rx'; - -const Observable = Rx.Observable; - -it('should infer correctly', () => { - const o = Observable.of('a', 'b', 'c').partition((value, index) => true); // $ExpectType [Observable, Observable] - const p = Observable.of('a', 'b', 'c').partition(() => true); // $ExpectType [Observable, Observable] -}); - -it('should accept a thisArg parameter', () => { - const o = Observable.of('a', 'b', 'c').partition(() => true, 5); // $ExpectType [Observable, Observable] -}); - -it('should enforce types', () => { - const o = Observable.of('a', 'b', 'c').partition(); // $ExpectError -}); - -it('should enforce predicate types', () => { - const o = Observable.of('a', 'b', 'c').partition('nope'); // $ExpectError - const p = Observable.of('a', 'b', 'c').partition((value: number) => true); // $ExpectError - const q = Observable.of('a', 'b', 'c').partition((value, index: string) => true); // $ExpectError -}); diff --git a/spec-dtslint/operators/publish-spec.ts b/spec-dtslint/operators/publish-spec.ts index 307d6192da..d68ef8331f 100644 --- a/spec-dtslint/operators/publish-spec.ts +++ b/spec-dtslint/operators/publish-spec.ts @@ -6,8 +6,7 @@ it('should support empty parameter', () => { // the next version infers Observable. It's not possible to specify // an upper bound for the TypeScript version used by dtslint, so an // expectation cannot be applied. - // TODO: put the test back after Typescript > 3.2 - const a = of(1, 2, 3).pipe(publish()); + const a = of(1, 2, 3).pipe(publish()); // $ExpectType Observable }); it('should infer when type is specified', () => { diff --git a/spec-dtslint/operators/publishLast-spec.ts b/spec-dtslint/operators/publishLast-spec.ts index 90c9db19cb..642f5e0898 100644 --- a/spec-dtslint/operators/publishLast-spec.ts +++ b/spec-dtslint/operators/publishLast-spec.ts @@ -6,8 +6,7 @@ it('should accept empty parameter', () => { // the next version infers Observable. It's not possible to specify // an upper bound for the TypeScript version used by dtslint, so an // expectation cannot be applied. - // TODO: put the test back after Typescript > 3.2 - const a = of(1, 2, 3).pipe(publishLast()); + const a = of(1, 2, 3).pipe(publishLast()); // $ExpectType Observable }); it('should infer when type is specified', () => { diff --git a/spec-dtslint/script.js b/spec-dtslint/script.js new file mode 100644 index 0000000000..f2a2fcaea9 --- /dev/null +++ b/spec-dtslint/script.js @@ -0,0 +1,4 @@ +const { execSync } = require('child_process'); +const path = require('path'); +const local = path.resolve('./node_modules/typescript/lib'); +execSync(`dtslint --localTs ${local} --expectOnly ./spec-dtslint`); \ No newline at end of file diff --git a/spec-dtslint/util/pipe-spec.ts b/spec-dtslint/util/pipe-spec.ts index 05e8b8773a..b08141ed2d 100644 --- a/spec-dtslint/util/pipe-spec.ts +++ b/spec-dtslint/util/pipe-spec.ts @@ -2,18 +2,18 @@ import { pipe, UnaryFunction, of, Observable } from 'rxjs'; /** * Used to keep the tests uncluttered. - * + * * Returns a `UnaryFunction` with the * specified literal type parameters. * That is, `a('0', '1')` returns `UnaryFunction<'0', '1'>`. * That means that the `a` function can be used to create consecutive * arguments that are either compatible or incompatible. - * + * * ```js * a('0', '1'), a('1', '2') // OK * a('0', '1'), a('#', '2') // Error '1' is not compatible with '#' * ``` - * + * * @param {string} input The `UnaryFunction` input type parameter * @param {string} output The `UnaryFunction` output type parameter */ diff --git a/spec/Notification-spec.ts b/spec/Notification-spec.ts index cbf8094ce3..69b1a9d058 100644 --- a/spec/Notification-spec.ts +++ b/spec/Notification-spec.ts @@ -1,8 +1,6 @@ import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; import { expectObservable } from './helpers/marble-testing'; - -const Notification = Rx.Notification; +import { Notification, Subscriber } from 'rxjs'; /** @test {Notification} */ describe('Notification', () => { @@ -147,7 +145,7 @@ describe('Notification', () => { const value = 'a'; let observed = false; const n = Notification.createNext(value); - const observer = Rx.Subscriber.create((x: string) => { + const observer = Subscriber.create((x: string) => { expect(x).to.equal(value); observed = true; }, (err: any) => { @@ -163,7 +161,7 @@ describe('Notification', () => { it('should accept observer for error Notification', () => { let observed = false; const n = Notification.createError(); - const observer = Rx.Subscriber.create((x: string) => { + const observer = Subscriber.create((x: string) => { throw 'should not be called'; }, (err: any) => { observed = true; @@ -178,7 +176,7 @@ describe('Notification', () => { it('should accept observer for complete Notification', () => { let observed = false; const n = Notification.createComplete(); - const observer = Rx.Subscriber.create((x: string) => { + const observer = Subscriber.create((x: string) => { throw 'should not be called'; }, (err: any) => { throw 'should not be called'; @@ -242,7 +240,7 @@ describe('Notification', () => { const value = 'a'; let observed = false; const n = Notification.createNext(value); - const observer = Rx.Subscriber.create((x: string) => { + const observer = Subscriber.create((x: string) => { expect(x).to.equal(value); observed = true; }, (err: any) => { @@ -258,7 +256,7 @@ describe('Notification', () => { it('should observe for error Notification', () => { let observed = false; const n = Notification.createError(); - const observer = Rx.Subscriber.create((x: any) => { + const observer = Subscriber.create((x: any) => { throw 'should not be called'; }, (err: any) => { observed = true; @@ -273,7 +271,7 @@ describe('Notification', () => { it('should observe for complete Notification', () => { let observed = false; const n = Notification.createComplete(); - const observer = Rx.Subscriber.create((x: any) => { + const observer = Subscriber.create((x: any) => { throw 'should not be called'; }, (err: any) => { throw 'should not be called'; diff --git a/spec/Observable-spec.ts b/spec/Observable-spec.ts index 872a57d852..5280f4a4b6 100644 --- a/spec/Observable-spec.ts +++ b/spec/Observable-spec.ts @@ -1,18 +1,11 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; -import * as Rx from 'rxjs/Rx'; import { Observer, TeardownLogic } from '../src/internal/types'; import { cold, expectObservable, expectSubscriptions } from './helpers/marble-testing'; -import { map } from '../src/internal/operators/map'; -import { noop } from '../src/internal/util/noop'; -import { NEVER } from '../src/internal/observable/never'; -import { Subscriber } from '../src/internal/Subscriber'; -import { Operator } from '../src/internal/Operator'; +import { Observable, config, Subscription, noop, Subscriber, Operator, NEVER, Subject, of, throwError, empty } from 'rxjs'; +import { map, multicast, refCount, filter, count, tap, combineLatest, concat, merge, race, zip } from 'rxjs/operators'; declare const asDiagram: any, rxTestScheduler: any; -const Observable = Rx.Observable; - -declare const __root__: any; function expectFullObserver(val: any) { expect(val).to.be.a('object'); @@ -25,10 +18,10 @@ function expectFullObserver(val: any) { /** @test {Observable} */ describe('Observable', () => { let originalConfigPromise: any; - before(() => originalConfigPromise = Rx.config.Promise); + before(() => originalConfigPromise = config.Promise); after(() => { - Rx.config.Promise = originalConfigPromise; + config.Promise = originalConfigPromise; originalConfigPromise = null; }); @@ -64,7 +57,7 @@ describe('Observable', () => { describe('forEach', () => { it('should iterate and return a Promise', (done) => { const expected = [1, 2, 3]; - const result = Observable.of(1, 2, 3).forEach(function (x) { + const result = of(1, 2, 3).forEach(function (x) { expect(x).to.equal(expected.shift()); }, Promise) .then(() => { @@ -75,7 +68,7 @@ describe('Observable', () => { }); it('should reject promise when in error', (done) => { - Observable.throwError('bad').forEach((x) => { + throwError('bad').forEach((x) => { done(new Error('should not be called')); }, Promise).then(() => { done(new Error('should not complete')); @@ -88,12 +81,12 @@ describe('Observable', () => { it('should allow Promise to be globally configured', (done) => { let wasCalled = false; - Rx.config.Promise = function MyPromise(callback: any) { + config.Promise = function MyPromise(callback: any) { wasCalled = true; return new Promise(callback); } as any; - Observable.of(42).forEach((x) => { + of(42).forEach((x) => { expect(x).to.equal(42); }).then(() => { expect(wasCalled).to.be.true; @@ -104,7 +97,7 @@ describe('Observable', () => { it('should reject promise if nextHandler throws', (done) => { const results: number[] = []; - Observable.of(1, 2, 3).forEach((x) => { + of(1, 2, 3).forEach((x) => { if (x === 3) { throw new Error('NO THREES!'); } @@ -230,7 +223,7 @@ describe('Observable', () => { expect(unsubscribeCalled).to.be.false; - Observable.empty().subscribe(); + empty().subscribe(); expect(unsubscribeCalled).to.be.false; }); @@ -251,7 +244,7 @@ describe('Observable', () => { expect(unsubscribeCalled).to.be.false; - Observable.empty().subscribe(observer); + empty().subscribe(observer); expect(unsubscribeCalled).to.be.false; }); @@ -310,7 +303,7 @@ describe('Observable', () => { const sub = source.subscribe(() => { //noop }); - expect(sub instanceof Rx.Subscription).to.be.true; + expect(sub instanceof Subscription).to.be.true; expect(unsubscribeCalled).to.be.false; expect(sub.unsubscribe).to.be.a('function'); @@ -333,7 +326,7 @@ describe('Observable', () => { done(); }; }) - .do(() => times += 1) + .pipe(tap(() => times += 1)) .subscribe( function () { if (times === 2) { @@ -364,7 +357,7 @@ describe('Observable', () => { done(); }; }) - .do(() => times += 1) + .pipe(tap(() => times += 1)) .subscribe( function () { if (times === 2) { @@ -395,7 +388,7 @@ describe('Observable', () => { done(); }; }) - .do(() => times += 1) + .pipe(tap(() => times += 1)) .subscribe( function () { if (times === 2) { @@ -420,7 +413,7 @@ describe('Observable', () => { } }; - Observable.of(1).subscribe(o); + of(1).subscribe(o); }); it('should accept an anonymous observer with just an error function and call the error function in the context' + @@ -435,7 +428,7 @@ describe('Observable', () => { } }; - Observable.throwError('bad').subscribe(o); + throwError('bad').subscribe(o); }); it('should accept an anonymous observer with just a complete function and call the complete function in the' + @@ -449,12 +442,12 @@ describe('Observable', () => { } }; - Observable.empty().subscribe(o); + empty().subscribe(o); }); it('should accept an anonymous observer with no functions at all', () => { expect(() => { - Observable.empty().subscribe({}); + empty().subscribe({}); }).not.to.throw(); }); @@ -473,7 +466,7 @@ describe('Observable', () => { done(); }; }) - .do(() => times += 1) + .pipe(tap(() => times += 1)) .subscribe({ next() { if (times === 2) { @@ -502,7 +495,7 @@ describe('Observable', () => { done(); }; }) - .do(() => times += 1) + .pipe(tap(() => times += 1)) .subscribe({ next() { if (times === 2) { @@ -533,7 +526,7 @@ describe('Observable', () => { done(); }; }) - .do(() => times += 1) + .pipe(tap(() => times += 1)) .subscribe({ next() { if (times === 2) { @@ -561,10 +554,10 @@ describe('Observable', () => { warnCalledWith.push(args); }; - Rx.config.useDeprecatedSynchronousErrorHandling = true; + config.useDeprecatedSynchronousErrorHandling = true; expect(warnCalledWith.length).to.equal(1); - Rx.config.useDeprecatedSynchronousErrorHandling = false; + config.useDeprecatedSynchronousErrorHandling = false; expect(logCalledWith.length).to.equal(1); console.log = _log; @@ -576,12 +569,12 @@ describe('Observable', () => { beforeEach(() => { const _warn = console.warn; console.warn = noop; - Rx.config.useDeprecatedSynchronousErrorHandling = true; + config.useDeprecatedSynchronousErrorHandling = true; console.warn = _warn; }); it('should throw synchronously', () => { - expect(() => Observable.throwError(new Error()).subscribe()) + expect(() => throwError(new Error()).subscribe()) .to.throw(); }); @@ -590,7 +583,7 @@ describe('Observable', () => { observer.next(1); }); - const sink = Rx.Subscriber.create(() => { + const sink = Subscriber.create(() => { throw 'error!'; }); @@ -602,7 +595,7 @@ describe('Observable', () => { afterEach(() => { const _log = console.log; console.log = noop; - Rx.config.useDeprecatedSynchronousErrorHandling = false; + config.useDeprecatedSynchronousErrorHandling = false; console.log = _log; }); }); @@ -610,12 +603,12 @@ describe('Observable', () => { describe('pipe', () => { it('should exist', () => { - const source = Observable.of('test'); + const source = of('test'); expect(source.pipe).to.be.a('function'); }); it('should pipe multiple operations', (done) => { - Observable.of('test') + of('test') .pipe( map((x) => x + x), map((x) => x + '!!!') @@ -630,7 +623,7 @@ describe('Observable', () => { }); it('should return the same observable if there are no arguments', () => { - const source = Observable.of('test'); + const source = of('test'); const result = source.pipe(); expect(result).to.equal(source); }); @@ -655,7 +648,7 @@ describe('Observable.create', () => { it('should provide an observer to the function', () => { let called = false; - const result = Observable.create((observer: Rx.Observer) => { + const result = Observable.create((observer: Observer) => { called = true; expectFullObserver(observer); observer.complete(); @@ -686,13 +679,13 @@ describe('Observable.create', () => { /** @test {Observable} */ describe('Observable.lift', () => { - class MyCustomObservable extends Rx.Observable { + class MyCustomObservable extends Observable { static from(source: any) { const observable = new MyCustomObservable(); - observable.source = >source; + observable.source = >source; return observable; } - lift(operator: Rx.Operator): Rx.Observable { + lift(operator: Operator): Observable { const observable = new MyCustomObservable(); (observable).source = this; (observable).operator = operator; @@ -724,7 +717,7 @@ describe('Observable.lift', () => { observer.next(2); observer.next(3); observer.complete(); - }).map((x) => { return 10 * x; }); + }).pipe(map((x) => { return 10 * x; })); expect(result instanceof MyCustomObservable).to.be.true; @@ -741,15 +734,16 @@ describe('Observable.lift', () => { }); it('should compose through multicast and refCount', (done) => { - const result = new MyCustomObservable((observer) => { + const result = new MyCustomObservable((observer) => { observer.next(1); observer.next(2); observer.next(3); observer.complete(); - }) - .multicast(() => new Rx.Subject()) - .refCount() - .map((x) => { return 10 * x; }); + }).pipe( + multicast(() => new Subject()), + refCount(), + map(x => 10 * x), + ); expect(result instanceof MyCustomObservable).to.be.true; @@ -766,13 +760,14 @@ describe('Observable.lift', () => { }); it('should compose through multicast with selector function', (done) => { - const result = new MyCustomObservable((observer) => { + const result = new MyCustomObservable((observer) => { observer.next(1); observer.next(2); observer.next(3); observer.complete(); - }) - .multicast(() => new Rx.Subject(), (shared) => shared.map((x) => { return 10 * x; })); + }).pipe( + multicast(() => new Subject(), shared => shared.pipe(map(x => 10 * x))) + ); expect(result instanceof MyCustomObservable).to.be.true; @@ -793,7 +788,9 @@ describe('Observable.lift', () => { const e2 = cold('--1--2-3-4---| '); const expected = '--A-BC-D-EF-G-H-|'; - const result = MyCustomObservable.from(e1).combineLatest(e2, (a, b) => String(a) + String(b)); + const result = MyCustomObservable.from(e1).pipe( + combineLatest(e2, (a, b) => String(a) + String(b)) + ); expect(result instanceof MyCustomObservable).to.be.true; @@ -807,7 +804,9 @@ describe('Observable.lift', () => { const e2 = cold('--x---y--|'); const expected = '--a--b---x---y--|'; - const result = MyCustomObservable.from(e1).concat(e2, rxTestScheduler); + const result = MyCustomObservable.from(e1).pipe( + concat(e2, rxTestScheduler) + ); expect(result instanceof MyCustomObservable).to.be.true; @@ -819,7 +818,9 @@ describe('Observable.lift', () => { const e2 = cold('--x--y-|'); const expected = '-ax-by-|'; - const result = MyCustomObservable.from(e1).merge(e2, rxTestScheduler); + const result = MyCustomObservable.from(e1).pipe( + merge(e2, rxTestScheduler) + ); expect(result instanceof MyCustomObservable).to.be.true; @@ -833,7 +834,10 @@ describe('Observable.lift', () => { const e2subs = '^ !'; const expected = '---a-----b-----c----|'; - const result = MyCustomObservable.from(e1).race(e2); + const result = MyCustomObservable.from(e1).pipe( + // TODO: remove after race typings are fixed. + race(e2) as any + ); expect(result instanceof MyCustomObservable).to.be.true; @@ -847,7 +851,9 @@ describe('Observable.lift', () => { const e2 = cold('--1--2-3-4---| '); const expected = ('--A--B----C-D| '); - const result = MyCustomObservable.from(e1).zip(e2, (a, b) => String(a) + String(b)); + const result = MyCustomObservable.from(e1).pipe( + zip(e2, (a, b) => String(a) + String(b)) + ); expect(result instanceof MyCustomObservable).to.be.true; @@ -861,7 +867,7 @@ describe('Observable.lift', () => { // The custom Subscriber const log: Array = []; - class LogSubscriber extends Rx.Subscriber { + class LogSubscriber extends Subscriber { next(value?: T): void { log.push('next ' + value); if (!this.isStopped) { @@ -871,18 +877,18 @@ describe('Observable.lift', () => { } // The custom Operator - class LogOperator implements Rx.Operator { - constructor(private childOperator: Rx.Operator) { + class LogOperator implements Operator { + constructor(private childOperator: Operator) { } - call(subscriber: Rx.Subscriber, source: any): TeardownLogic { + call(subscriber: Subscriber, source: any): TeardownLogic { return this.childOperator.call(new LogSubscriber(subscriber), source); } } // The custom Observable class LogObservable extends Observable { - lift(operator: Rx.Operator): Rx.Observable { + lift(operator: Operator): Observable { const observable = new LogObservable(); (observable).source = this; (observable).operator = new LogOperator(operator); @@ -896,10 +902,11 @@ describe('Observable.lift', () => { observer.next(2); observer.next(3); observer.complete(); - }) - .map((x) => { return 10 * x; }) - .filter((x) => { return x > 15; }) - .count(); + }).pipe( + map(x => 10 * x), + filter(x => x > 15), + count(), + ); expect(result instanceof LogObservable).to.be.true; diff --git a/spec/Subject-spec.ts b/spec/Subject-spec.ts index 195ffe4b2c..be89a9074a 100644 --- a/spec/Subject-spec.ts +++ b/spec/Subject-spec.ts @@ -1,9 +1,8 @@ import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; import { hot, expectObservable } from './helpers/marble-testing'; - -const Subject = Rx.Subject; -const Observable = Rx.Observable; +import { Subject, ObjectUnsubscribedError, Observable, AsyncSubject, Observer, of } from 'rxjs'; +import { AnonymousSubject } from 'rxjs/internal/Subject'; +import { delay } from 'rxjs/operators'; /** @test {Subject} */ describe('Subject', () => { @@ -264,7 +263,7 @@ describe('Subject', () => { expect(false).to.equal('should not throw error: ' + err.toString()); } ); - }).to.throw(Rx.ObjectUnsubscribedError); + }).to.throw(ObjectUnsubscribedError); expect(results1).to.deep.equal([1, 2, 3, 4, 5]); expect(results2).to.deep.equal([3, 4, 5]); @@ -281,7 +280,7 @@ describe('Subject', () => { subject.next('foo'); subject.unsubscribe(); - expect(() => subject.next('bar')).to.throw(Rx.ObjectUnsubscribedError); + expect(() => subject.next('bar')).to.throw(ObjectUnsubscribedError); done(); }); @@ -306,7 +305,7 @@ describe('Subject', () => { it('should have a static create function that works', () => { expect(Subject.create).to.be.a('function'); - const source = Observable.of(1, 2, 3, 4, 5); + const source = of(1, 2, 3, 4, 5); const nexts: number[] = []; const output: number[] = []; @@ -352,7 +351,7 @@ describe('Subject', () => { it('should have a static create function that works also to raise errors', () => { expect(Subject.create).to.be.a('function'); - const source = Observable.of(1, 2, 3, 4, 5); + const source = of(1, 2, 3, 4, 5); const nexts: number[] = []; const output: number[] = []; @@ -397,7 +396,7 @@ describe('Subject', () => { }); it('should be an Observer which can be given to Observable.subscribe', (done: MochaDone) => { - const source = Observable.of(1, 2, 3, 4, 5); + const source = of(1, 2, 3, 4, 5); const subject = new Subject(); const expected = [1, 2, 3, 4, 5]; @@ -414,8 +413,8 @@ describe('Subject', () => { }); it('should be usable as an Observer of a finite delayed Observable', (done: MochaDone) => { - const source = Rx.Observable.of(1, 2, 3).delay(50); - const subject = new Rx.Subject(); + const source = of(1, 2, 3).pipe(delay(50)); + const subject = new Subject(); const expected = [1, 2, 3]; @@ -432,24 +431,24 @@ describe('Subject', () => { }); it('should throw ObjectUnsubscribedError when emit after unsubscribed', () => { - const subject = new Rx.Subject(); + const subject = new Subject(); subject.unsubscribe(); expect(() => { subject.next('a'); - }).to.throw(Rx.ObjectUnsubscribedError); + }).to.throw(ObjectUnsubscribedError); expect(() => { subject.error('a'); - }).to.throw(Rx.ObjectUnsubscribedError); + }).to.throw(ObjectUnsubscribedError); expect(() => { subject.complete(); - }).to.throw(Rx.ObjectUnsubscribedError); + }).to.throw(ObjectUnsubscribedError); }); it('should not next after completed', () => { - const subject = new Rx.Subject(); + const subject = new Subject(); const results: string[] = []; subject.subscribe(x => results.push(x), null, () => results.push('C')); subject.next('a'); @@ -460,7 +459,7 @@ describe('Subject', () => { it('should not next after error', () => { const error = new Error('wut?'); - const subject = new Rx.Subject(); + const subject = new Subject(); const results: string[] = []; subject.subscribe(x => results.push(x), (err) => results.push(err)); subject.next('a'); @@ -471,7 +470,7 @@ describe('Subject', () => { describe('asObservable', () => { it('should hide subject', () => { - const subject = new Rx.Subject(); + const subject = new Subject(); const observable = subject.asObservable(); expect(subject).not.to.equal(observable); @@ -509,7 +508,7 @@ describe('Subject', () => { it('should work with inherited subject', () => { const results: (number | string)[] = []; - const subject = new Rx.AsyncSubject(); + const subject = new AsyncSubject(); subject.next(42); subject.complete(); @@ -525,15 +524,15 @@ describe('Subject', () => { describe('AnonymousSubject', () => { it('should be exposed', () => { - expect(Rx.AnonymousSubject).to.be.a('function'); + expect(AnonymousSubject).to.be.a('function'); }); it('should not eager', () => { let subscribed = false; - const subject = Rx.Subject.create(null, new Rx.Observable((observer: Rx.Observer) => { + const subject = Subject.create(null, new Observable((observer: Observer) => { subscribed = true; - const subscription = Rx.Observable.of('x').subscribe(observer); + const subscription = of('x').subscribe(observer); return () => { subscription.unsubscribe(); }; diff --git a/spec/Subscription-spec.ts b/spec/Subscription-spec.ts index 3d9ac956a2..1532768a91 100644 --- a/spec/Subscription-spec.ts +++ b/spec/Subscription-spec.ts @@ -1,56 +1,53 @@ import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; - -const Observable = Rx.Observable; -const Subscription = Rx.Subscription; +import { Observable, UnsubscriptionError, Subscription, merge } from 'rxjs'; /** @test {Subscription} */ describe('Subscription', () => { - it('should not leak', (done: MochaDone) => { + it('should not leak', done => { const tearDowns: number[] = []; - const source1 = Observable.create((observer: Rx.Observer) => { + const source1 = new Observable(() => { return () => { tearDowns.push(1); }; }); - const source2 = Observable.create((observer: Rx.Observer) => { + const source2 = new Observable(() => { return () => { tearDowns.push(2); throw new Error('oops, I am a bad unsubscribe!'); }; }); - const source3 = Observable.create((observer: Rx.Observer) => { + const source3 = new Observable(() => { return () => { tearDowns.push(3); }; }); - const subscription = Observable.merge(source1, source2, source3).subscribe(); + const subscription = merge(source1, source2, source3).subscribe(); setTimeout(() => { expect(() => { subscription.unsubscribe(); - }).to.throw(Rx.UnsubscriptionError); + }).to.throw(UnsubscriptionError); expect(tearDowns).to.deep.equal([1, 2, 3]); done(); }); }); - it('should not leak when adding a bad custom subscription to a subscription', (done: MochaDone) => { + it('should not leak when adding a bad custom subscription to a subscription', done => { const tearDowns: number[] = []; const sub = new Subscription(); - const source1 = Observable.create((observer: Rx.Observer) => { + const source1 = new Observable(() => { return () => { tearDowns.push(1); }; }); - const source2 = Observable.create((observer: Rx.Observer) => { + const source2 = new Observable(() => { return () => { tearDowns.push(2); sub.add(({ @@ -62,18 +59,18 @@ describe('Subscription', () => { }; }); - const source3 = Observable.create((observer: Rx.Observer) => { + const source3 = new Observable(() => { return () => { tearDowns.push(3); }; }); - sub.add(Observable.merge(source1, source2, source3).subscribe()); + sub.add(merge(source1, source2, source3).subscribe()); setTimeout(() => { expect(() => { sub.unsubscribe(); - }).to.throw(Rx.UnsubscriptionError); + }).to.throw(UnsubscriptionError); expect(tearDowns).to.deep.equal([1, 2, 3]); done(); }); diff --git a/spec/exports-spec.ts b/spec/exports-spec.ts.disabled similarity index 95% rename from spec/exports-spec.ts rename to spec/exports-spec.ts.disabled index b30e92db14..1ca1fb15e7 100644 --- a/spec/exports-spec.ts +++ b/spec/exports-spec.ts.disabled @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { bindCallback, bindNodeCallback, combineLatest, concat, defer, empty, forkJoin, from, fromEvent, fromEventPattern, - iif, interval, merge, NEVER, of, onErrorResumeNext, pairs, race, range, throwError, timer, using, zip } from 'rxjs'; + iif, interval, merge, of, onErrorResumeNext, pairs, race, range, throwError, timer, using, zip } from 'rxjs'; import * as Rx from 'rxjs/Rx'; describe('exports', () => { diff --git a/spec/helpers/observableMatcher.ts b/spec/helpers/observableMatcher.ts new file mode 100644 index 0000000000..b2965117d2 --- /dev/null +++ b/spec/helpers/observableMatcher.ts @@ -0,0 +1,48 @@ +import * as _ from 'lodash'; + +function stringify(x: any): string { + return JSON.stringify(x, function (key: string, value: any) { + if (Array.isArray(value)) { + return '[' + value + .map(function (i) { + return '\n\t' + stringify(i); + }) + '\n]'; + } + return value; + }) + .replace(/\\"/g, '"') + .replace(/\\t/g, '\t') + .replace(/\\n/g, '\n'); +} + +function deleteErrorNotificationStack(marble: any) { + const { notification } = marble; + if (notification) { + const { kind, error } = notification; + if (kind === 'E' && error instanceof Error) { + notification.error = { name: error.name, message: error.message }; + } + } + return marble; +} + +export function observableMatcher(actual: any, expected: any) { + if (Array.isArray(actual) && Array.isArray(expected)) { + actual = actual.map(deleteErrorNotificationStack); + expected = expected.map(deleteErrorNotificationStack); + const passed = _.isEqual(actual, expected); + if (passed) { + return; + } + + let message = '\nExpected \n'; + actual.forEach((x: any) => message += `\t${stringify(x)}\n`); + + message += '\t\nto deep equal \n'; + expected.forEach((x: any) => message += `\t${stringify(x)}\n`); + + chai.assert(passed, message); + } else { + chai.assert.deepEqual(actual, expected); + } +} diff --git a/spec/helpers/test-helper.ts b/spec/helpers/test-helper.ts index 3c8f598f64..084cf6e96c 100644 --- a/spec/helpers/test-helper.ts +++ b/spec/helpers/test-helper.ts @@ -1,12 +1,11 @@ declare const global: any; -import * as Rx from 'rxjs/Rx'; -import { ObservableInput } from 'rxjs'; -import { iterator } from 'rxjs/symbol/iterator'; -import { root } from 'rxjs/util/root'; +import { ObservableInput, of, asyncScheduler, Observable } from 'rxjs'; +import { iterator } from 'rxjs/internal/symbol/iterator'; +import { root } from 'rxjs/internal/util/root'; import $$symbolObservable from 'symbol-observable'; -export function lowerCaseO(...args: Array): Rx.Observable { +export function lowerCaseO(...args: Array): Observable { const o = { subscribe(observer: any) { args.forEach(v => observer.next(v)); @@ -24,9 +23,9 @@ export function lowerCaseO(...args: Array): Rx.Observable { return o; } -export const createObservableInputs = (value: T) => Rx.Observable.of>( - Rx.Observable.of(value), - Rx.Observable.of(value, Rx.Scheduler.async), +export const createObservableInputs = (value: T) => of>( + of(value), + of(value, asyncScheduler), [value], Promise.resolve(value), ({ @@ -42,7 +41,7 @@ export const createObservableInputs = (value: T) => Rx.Observable.of({ [$$symbolObservable]: () => Rx.Observable.of(value) }) + ({ [$$symbolObservable]: () => of(value) }) ); global.__root__ = root; diff --git a/spec/helpers/testScheduler-ui.ts b/spec/helpers/testScheduler-ui.ts index 7d78ff7cc9..7bad60d1fe 100644 --- a/spec/helpers/testScheduler-ui.ts +++ b/spec/helpers/testScheduler-ui.ts @@ -1,11 +1,8 @@ import * as _ from 'lodash'; -//import * as commonInterface from 'mocha/lib/interfaces/common'; -//import * as escapeRe from 'escape-string-regexp'; import * as chai from 'chai'; import * as sinonChai from 'sinon-chai'; - -import * as Rx from 'rxjs/Rx'; import * as marble from './marble-testing'; +import { TestScheduler } from 'rxjs/testing'; //tslint:disable:no-var-requires no-require-imports const commonInterface = require('mocha/lib/interfaces/common'); @@ -200,7 +197,7 @@ module.exports = function(suite: any) { if (fn && fn.length === 0) { modified = function () { - context.rxTestScheduler = new Rx.TestScheduler(observableMatcher); + context.rxTestScheduler = new TestScheduler(observableMatcher); try { fn(); diff --git a/spec/observables/IteratorObservable-spec.ts b/spec/observables/IteratorObservable-spec.ts index 7b952efcb4..01f6149226 100644 --- a/spec/observables/IteratorObservable-spec.ts +++ b/spec/observables/IteratorObservable-spec.ts @@ -1,10 +1,10 @@ import { expect } from 'chai'; import { queue } from 'rxjs/scheduler/queue'; import { fromIterable } from 'rxjs/internal/observable/fromIterable'; +import { iterator as symbolIterator } from 'rxjs/internal/symbol/iterator'; import { TestScheduler } from 'rxjs/testing'; import { Notification, queueScheduler, Subscriber } from 'rxjs'; -import { observeOn, materialize, take } from 'rxjs/operators'; -import * as Rx from 'rxjs/Rx'; +import { observeOn, materialize, take, toArray } from 'rxjs/operators'; declare const expectObservable: any; declare const rxTestScheduler: TestScheduler; @@ -43,8 +43,8 @@ describe('fromIterable', () => { const e1 = fromIterable(new Int32Array([10, 20]), undefined).pipe(observeOn(rxTestScheduler)); let v1, v2: Array>; - e1.pipe(materialize()).toArray().subscribe((x) => v1 = x); - e1.pipe(materialize()).toArray().subscribe((x) => v2 = x); + e1.pipe(materialize(), toArray()).subscribe((x) => v1 = x); + e1.pipe(materialize(), toArray()).subscribe((x) => v2 = x); rxTestScheduler.flush(); expect(v1).to.deep.equal(expected); @@ -63,7 +63,7 @@ describe('fromIterable', () => { }; const iterable = { - [Rx.Symbol.iterator]() { + [symbolIterator]() { return iterator; } }; @@ -94,7 +94,7 @@ describe('fromIterable', () => { }; const iterable = { - [Rx.Symbol.iterator]() { + [symbolIterator]() { return iterator; } }; diff --git a/spec/observables/ScalarObservable-spec.ts b/spec/observables/ScalarObservable-spec.ts deleted file mode 100644 index 1e3d785b0d..0000000000 --- a/spec/observables/ScalarObservable-spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { expect } from 'chai'; -import { of as scalar } from 'rxjs'; -import { TestScheduler } from 'rxjs/testing'; - -declare const rxTestScheduler: TestScheduler; - -describe('scalar', () => { - it('should create expose a value property', () => { - const s = scalar(1); - expect((s as any).value).to.equal(1); - }); - - it('should set `_isScalar` to true when NOT called with a Scheduler', () => { - const s = scalar(1); - expect(s._isScalar).to.be.true; - }); -}); diff --git a/spec/observables/concat-spec.ts b/spec/observables/concat-spec.ts index f1ccb69a5b..2ecee6d83e 100644 --- a/spec/observables/concat-spec.ts +++ b/spec/observables/concat-spec.ts @@ -58,7 +58,9 @@ describe('static concat', () => { const expected = '--i-j-k-l---i-j-'; const unsub = ' !'; - const innerWrapped = inner.mergeMap((x) => of(x)); + const innerWrapped = inner.pipe( + mergeMap((x) => of(x)) + ); const result = concat(innerWrapped, innerWrapped, innerWrapped, innerWrapped) .pipe(mergeMap((x) => of(x))); diff --git a/spec/observables/dom/fetch-spec.ts b/spec/observables/dom/fetch-spec.ts new file mode 100644 index 0000000000..93923c7914 --- /dev/null +++ b/spec/observables/dom/fetch-spec.ts @@ -0,0 +1,196 @@ +import { fromFetch } from 'rxjs/fetch'; +import { expect } from 'chai'; +import { root } from '../../../src/internal/util/root'; + +const OK_RESPONSE = { + ok: true, +} as Response; + +function mockFetchImpl(input: string | Request, init?: RequestInit): Promise { + (mockFetchImpl as MockFetch).calls.push({ input, init }); + return new Promise((resolve, reject) => { + if (init.signal) { + init.signal.addEventListener('abort', () => { + reject(new MockDOMException()); + }); + } + return Promise.resolve(null).then(() => { + resolve((mockFetchImpl as any).respondWith); + }); + }); +} +(mockFetchImpl as MockFetch).reset = function (this: any) { + this.calls = [] as any[]; + this.respondWith = OK_RESPONSE; +}; +(mockFetchImpl as MockFetch).reset(); + +const mockFetch: MockFetch = mockFetchImpl as MockFetch; + +class MockDOMException {} + +class MockAbortController { + readonly signal = new MockAbortSignal(); + + abort() { + this.signal._signal(); + } + + constructor() { + MockAbortController.created++; + } + + static created = 0; + + static reset() { + MockAbortController.created = 0; + } +} + +class MockAbortSignal { + private _listeners: Function[] = []; + + aborted = false; + + addEventListener(name: 'abort', handler: Function) { + this._listeners.push(handler); + } + + removeEventListener(name: 'abort', handler: Function) { + const index = this._listeners.indexOf(handler); + if (index >= 0) { + this._listeners.splice(index, 1); + } + } + + _signal() { + this.aborted = true; + while (this._listeners.length > 0) { + this._listeners.shift()(); + } + } +} + +interface MockFetch { + (input: string | Request, init?: RequestInit): Promise; + calls: { input: string | Request, init: RequestInit | undefined }[]; + reset(): void; + respondWith: Response; +} + +describe('fromFetch', () => { + let _fetch: typeof fetch; + let _AbortController: AbortController; + + beforeEach(() => { + mockFetch.reset(); + if (root.fetch) { + _fetch = root.fetch; + } + root.fetch = mockFetch; + + MockAbortController.reset(); + if (root.AbortController) { + _AbortController = root.AbortController; + } + root.AbortController = MockAbortController; + }); + + afterEach(() => { + root.fetch = _fetch; + root.AbortController = _AbortController; + }); + + it('should exist', () => { + expect(fromFetch).to.be.a('function'); + }); + + it('should fetch', done => { + const fetch$ = fromFetch('/foo'); + expect(mockFetch.calls.length).to.equal(0); + expect(MockAbortController.created).to.equal(0); + + fetch$.subscribe({ + next: response => { + expect(response).to.equal(OK_RESPONSE); + }, + error: done, + complete: done, + }); + + expect(MockAbortController.created).to.equal(1); + expect(mockFetch.calls.length).to.equal(1); + expect(mockFetch.calls[0].input).to.equal('/foo'); + expect(mockFetch.calls[0].init.signal).not.to.be.undefined; + expect(mockFetch.calls[0].init.signal.aborted).to.be.false; + }); + + it('should handle Response that is not `ok`', done => { + mockFetch.respondWith = { + ok: false, + status: 400, + body: 'Bad stuff here' + } as any as Response; + + const fetch$ = fromFetch('/foo'); + expect(mockFetch.calls.length).to.equal(0); + expect(MockAbortController.created).to.equal(0); + + fetch$.subscribe({ + next: response => { + expect(response).to.equal(mockFetch.respondWith); + }, + complete: done, + error: done + }); + + expect(MockAbortController.created).to.equal(1); + expect(mockFetch.calls.length).to.equal(1); + expect(mockFetch.calls[0].input).to.equal('/foo'); + expect(mockFetch.calls[0].init.signal).not.to.be.undefined; + expect(mockFetch.calls[0].init.signal.aborted).to.be.false; + }); + + it('should abort when unsubscribed', () => { + const fetch$ = fromFetch('/foo'); + expect(mockFetch.calls.length).to.equal(0); + expect(MockAbortController.created).to.equal(0); + const subscription = fetch$.subscribe(); + + expect(MockAbortController.created).to.equal(1); + expect(mockFetch.calls.length).to.equal(1); + expect(mockFetch.calls[0].input).to.equal('/foo'); + expect(mockFetch.calls[0].init.signal).not.to.be.undefined; + expect(mockFetch.calls[0].init.signal.aborted).to.be.false; + + subscription.unsubscribe(); + expect(mockFetch.calls[0].init.signal.aborted).to.be.true; + }); + + it('should allow passing of init object', done => { + const myInit = {}; + const fetch$ = fromFetch('/foo', myInit); + fetch$.subscribe({ + error: done, + complete: done, + }); + expect(mockFetch.calls[0].init).to.equal(myInit); + expect(mockFetch.calls[0].init.signal).not.to.be.undefined; + }); + + it('should treat passed signals as a cancellation token which triggers an error', done => { + const controller = new MockAbortController(); + const signal = controller.signal as any; + const fetch$ = fromFetch('/foo', { signal }); + const subscription = fetch$.subscribe({ + error: err => { + expect(err).to.be.instanceof(MockDOMException); + done(); + } + }); + controller.abort(); + expect(mockFetch.calls[0].init.signal.aborted).to.be.true; + // The subscription will not be closed until the error fires when the promise resolves. + expect(subscription.closed).to.be.false; + }); +}); diff --git a/spec/observables/forkJoin-spec.ts b/spec/observables/forkJoin-spec.ts index b7be9ba7e9..0bbb31211e 100644 --- a/spec/observables/forkJoin-spec.ts +++ b/spec/observables/forkJoin-spec.ts @@ -2,6 +2,7 @@ import { expect } from 'chai'; import { Observable, forkJoin, of } from 'rxjs'; import { lowerCaseO } from '../helpers/test-helper'; import { hot, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; +import { AssertionError } from 'assert'; declare const type: Function; declare const asDiagram: Function; @@ -67,284 +68,441 @@ describe('forkJoin', () => { expect(results).to.deep.equal([18, 'done']); }); - it('should join the last values of the provided observables into an array', () => { + it('should accept single observable', () => { const e1 = forkJoin( - hot('--a--b--c--d--|'), - hot('(b|)'), - hot('--1--2--3--|') + hot('--a--b--c--d--|') ); const expected = '--------------(x|)'; - expectObservable(e1).toBe(expected, {x: ['d', 'b', '3']}); + expectObservable(e1).toBe(expected, {x: ['d']}); }); - it('should allow emit null or undefined', () => { - const e2 = forkJoin( - hot('--a--b--c--d--|', { d: null }), - hot('(b|)'), - hot('--1--2--3--|'), - hot('-----r--t--u--|', { u: undefined }) - ); - const expected2 = '--------------(x|)'; + describe('forkJoin([input1, input2, input3])', () => { + it('should join the last values of the provided observables into an array', () => { + const e1 = forkJoin([ + hot('--a--b--c--d--|'), + hot('(b|)'), + hot('--1--2--3--|') + ]); + const expected = '--------------(x|)'; - expectObservable(e2).toBe(expected2, {x: [null, 'b', '3', undefined]}); - }); + expectObservable(e1).toBe(expected, {x: ['d', 'b', '3']}); + }); - it('should accept single observable', () => { - const e1 = forkJoin( - hot('--a--b--c--d--|') - ); - const expected = '--------------(x|)'; + it('should allow emit null or undefined', () => { + const e2 = forkJoin([ + hot('--a--b--c--d--|', { d: null }), + hot('(b|)'), + hot('--1--2--3--|'), + hot('-----r--t--u--|', { u: undefined }) + ]); + const expected2 = '--------------(x|)'; - expectObservable(e1).toBe(expected, {x: ['d']}); - }); + expectObservable(e2).toBe(expected2, {x: [null, 'b', '3', undefined]}); + }); - it('should accept array of observable contains single', () => { - const e1 = forkJoin( - [hot('--a--b--c--d--|')] - ); - const expected = '--------------(x|)'; + it('should accept array of observable contains single', () => { + const e1 = forkJoin([hot('--a--b--c--d--|')]); + const expected = '--------------(x|)'; - expectObservable(e1).toBe(expected, {x: ['d']}); - }); + expectObservable(e1).toBe(expected, {x: ['d']}); + }); - it('should accept lowercase-o observables', () => { - const e1 = forkJoin( - hot('--a--b--c--d--|'), - hot('(b|)'), - lowerCaseO('1', '2', '3') - ); - const expected = '--------------(x|)'; + it('should accept lowercase-o observables', () => { + const e1 = forkJoin([ + hot('--a--b--c--d--|'), + hot('(b|)'), + lowerCaseO('1', '2', '3') + ]); + const expected = '--------------(x|)'; - expectObservable(e1).toBe(expected, {x: ['d', 'b', '3']}); - }); + expectObservable(e1).toBe(expected, {x: ['d', 'b', '3']}); + }); - it('should accept empty lowercase-o observables', () => { - const e1 = forkJoin( - hot('--a--b--c--d--|'), - hot('(b|)'), - lowerCaseO() - ); - const expected = '|'; + it('should accept empty lowercase-o observables', () => { + const e1 = forkJoin([ + hot('--a--b--c--d--|'), + hot('(b|)'), + lowerCaseO() + ]); + const expected = '|'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should accept promise', (done: MochaDone) => { - const e1 = forkJoin( - of(1), - Promise.resolve(2) - ); + it('should accept promise', done => { + const e1 = forkJoin([ + of(1), + Promise.resolve(2) + ]); - e1.subscribe((x: number[]) => { - expect(x).to.deep.equal([1, 2]); - }, - (err: any) => { - done(new Error('should not be called')); - }, () => { - done(); + e1.subscribe({ + next: x => expect(x).to.deep.equal([1, 2]), + complete: done, + }); }); - }); - it('should accept array of observables', () => { - const e1 = forkJoin( - [hot('--a--b--c--d--|'), + it('should accept array of observables', () => { + const e1 = forkJoin([ + hot('--a--b--c--d--|'), + hot('(b|)'), + hot('--1--2--3--|')]); + const expected = '--------------(x|)'; + + expectObservable(e1).toBe(expected, {x: ['d', 'b', '3']}); + }); + + it('should not emit if any of source observable is empty', () => { + const e1 = forkJoin([ + hot('--a--b--c--d--|'), hot('(b|)'), - hot('--1--2--3--|')] - ); - const expected = '--------------(x|)'; + hot('------------------|') + ]); + const expected = '------------------|'; - expectObservable(e1).toBe(expected, {x: ['d', 'b', '3']}); - }); + expectObservable(e1).toBe(expected); + }); - it('should not emit if any of source observable is empty', () => { - const e1 = forkJoin( - hot('--a--b--c--d--|'), - hot('(b|)'), - hot('------------------|') - ); - const expected = '------------------|'; + it('should complete early if any of source is empty and completes before than others', () => { + const e1 = forkJoin([ + hot('--a--b--c--d--|'), + hot('(b|)'), + hot('---------|') + ]); + const expected = '---------|'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should complete early if any of source is empty and completes before than others', () => { - const e1 = forkJoin( - hot('--a--b--c--d--|'), - hot('(b|)'), - hot('---------|') - ); - const expected = '---------|'; + it('should complete when all sources are empty', () => { + const e1 = forkJoin([ + hot('--------------|'), + hot('---------|') + ]); + const expected = '---------|'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should complete when all sources are empty', () => { - const e1 = forkJoin( - hot('--------------|'), - hot('---------|') - ); - const expected = '---------|'; + it('should not complete when only source never completes', () => { + const e1 = forkJoin([ + hot('--------------') + ]); + const expected = '-'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should not complete when only source never completes', () => { - const e1 = forkJoin( - hot('--------------') - ); - const expected = '-'; + it('should not complete when one of the sources never completes', () => { + const e1 = forkJoin([ + hot('--------------'), + hot('-a---b--c--|') + ]); + const expected = '-'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should not complete when one of the sources never completes', () => { - const e1 = forkJoin( - hot('--------------'), - hot('-a---b--c--|') - ); - const expected = '-'; + it('should complete when one of the sources never completes but other completes without values', () => { + const e1 = forkJoin([ + hot('--------------'), + hot('------|') + ]); + const expected = '------|'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should complete when one of the sources never completes but other completes without values', () => { - const e1 = forkJoin( - hot('--------------'), - hot('------|') - ); - const expected = '------|'; + it('should complete if source is not provided', () => { + const e1 = forkJoin(); + const expected = '|'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should complete if source is not provided', () => { - const e1 = forkJoin(); - const expected = '|'; + it('should complete if sources list is empty', () => { + const e1 = forkJoin([]); + const expected = '|'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should complete if sources list is empty', () => { - const e1 = forkJoin([]); - const expected = '|'; + it('should raise error when any of source raises error with empty observable', () => { + const e1 = forkJoin([ + hot('------#'), + hot('---------|')]); + const expected = '------#'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should raise error when any of source raises error with empty observable', () => { - const e1 = forkJoin( - hot('------#'), - hot('---------|')); - const expected = '------#'; + it('should raise error when any of source raises error with source that never completes', () => { + const e1 = forkJoin([ + hot('------#'), + hot('----------')]); + const expected = '------#'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should raise error when any of source raises error with source that never completes', () => { - const e1 = forkJoin( - hot('------#'), - hot('----------')); - const expected = '------#'; + it('should raise error when source raises error', () => { + const e1 = forkJoin([ + hot('------#'), + hot('---a-----|')]); + const expected = '------#'; - expectObservable(e1).toBe(expected); - }); + expectObservable(e1).toBe(expected); + }); - it('should raise error when source raises error', () => { - const e1 = forkJoin( - hot('------#'), - hot('---a-----|')); - const expected = '------#'; + it('should allow unsubscribing early and explicitly', () => { + const e1 = hot('--a--^--b--c---d-| '); + const e1subs = '^ ! '; + const e2 = hot('---e-^---f--g---h-|'); + const e2subs = '^ ! '; + const expected = '---------- '; + const unsub = ' ! '; - expectObservable(e1).toBe(expected); - }); + const result = forkJoin([e1, e2]); - it('should allow unsubscribing early and explicitly', () => { - const e1 = hot('--a--^--b--c---d-| '); - const e1subs = '^ ! '; - const e2 = hot('---e-^---f--g---h-|'); - const e2subs = '^ ! '; - const expected = '---------- '; - const unsub = ' ! '; + expectObservable(result, unsub).toBe(expected); + expectSubscriptions(e1.subscriptions).toBe(e1subs); + expectSubscriptions(e2.subscriptions).toBe(e2subs); + }); - const result = forkJoin(e1, e2); + it('should unsubscribe other Observables, when one of them errors', () => { + const e1 = hot('--a--^--b--c---d-| '); + const e1subs = '^ ! '; + const e2 = hot('---e-^---f--g-#'); + const e2subs = '^ ! '; + const expected = '---------# '; - expectObservable(result, unsub).toBe(expected); - expectSubscriptions(e1.subscriptions).toBe(e1subs); - expectSubscriptions(e2.subscriptions).toBe(e2subs); + const result = forkJoin([e1, e2]); + + expectObservable(result).toBe(expected); + expectSubscriptions(e1.subscriptions).toBe(e1subs); + expectSubscriptions(e2.subscriptions).toBe(e2subs); + }); }); - it('should unsubscribe other Observables, when one of them errors', () => { - const e1 = hot('--a--^--b--c---d-| '); - const e1subs = '^ ! '; - const e2 = hot('---e-^---f--g-#'); - const e2subs = '^ ! '; - const expected = '---------# '; + describe('forkJoin({ foo, bar, baz })', () => { + it('should join the last values of the provided observables into an array', () => { + const e1 = forkJoin({ + foo: hot('--a--b--c--d--|'), + bar: hot('(b|)'), + baz: hot('--1--2--3--|') + }); + const expected = '--------------(x|)'; - const result = forkJoin(e1, e2); + expectObservable(e1).toBe(expected, {x: { foo: 'd', bar: 'b', baz: '3' } }); + }); - expectObservable(result).toBe(expected); - expectSubscriptions(e1.subscriptions).toBe(e1subs); - expectSubscriptions(e2.subscriptions).toBe(e2subs); - }); + it('should allow emit null or undefined', () => { + const e2 = forkJoin({ + foo: hot('--a--b--c--d--|', { d: null }), + bar: hot('(b|)'), + baz: hot('--1--2--3--|'), + qux: hot('-----r--t--u--|', { u: undefined }) + }); + const expected2 = '--------------(x|)'; - type('should support promises', () => { - /* tslint:disable:no-unused-variable */ - let a: Promise; - let b: Promise; - let c: Promise; - let o1: Observable<[number, string, boolean]> = forkJoin(a, b, c); - let o2: Observable = forkJoin(a, b, c, (aa: number, bb: string, cc: boolean) => !!aa && !!bb && cc); - /* tslint:enable:no-unused-variable */ - }); + expectObservable(e2).toBe(expected2, {x: { foo: null, bar: 'b', baz: '3', qux: undefined } }); + }); - type('should support observables', () => { - /* tslint:disable:no-unused-variable */ - let a: Observable; - let b: Observable; - let c: Observable; - let o1: Observable<[number, string, boolean]> = forkJoin(a, b, c); - let o2: Observable = forkJoin(a, b, c, (aa: number, bb: string, cc: boolean) => !!aa && !!bb && cc); - /* tslint:enable:no-unused-variable */ - }); + it('should accept array of observable contains single', () => { + const e1 = forkJoin({ + foo: hot('--a--b--c--d--|') + }); + const expected = '--------------(x|)'; - type('should support mixed observables and promises', () => { - /* tslint:disable:no-unused-variable */ - let a: Promise; - let b: Observable; - let c: Promise; - let d: Observable; - let o1: Observable<[number, string, boolean, string[]]> = forkJoin(a, b, c, d); - /* tslint:enable:no-unused-variable */ - }); + expectObservable(e1).toBe(expected, {x: { foo: 'd' }}); + }); - type('should support arrays of promises', () => { - /* tslint:disable:no-unused-variable */ - let a: Promise[]; - let o1: Observable = forkJoin(a); - let o2: Observable = forkJoin(...a); - /* tslint:enable:no-unused-variable */ - }); + it('should accept lowercase-o observables', () => { + const e1 = forkJoin({ + foo: hot('--a--b--c--d--|'), + bar: hot('(b|)'), + baz: lowerCaseO('1', '2', '3') + }); + const expected = '--------------(x|)'; - type('should support arrays of observables', () => { - /* tslint:disable:no-unused-variable */ - let a: Observable[]; - let o1: Observable = forkJoin(a); - let o2: Observable = forkJoin(...a); - /* tslint:enable:no-unused-variable */ - }); + expectObservable(e1).toBe(expected, {x: { foo: 'd', bar: 'b', baz: '3' }}); + }); - type('should return Array when given a single promise', () => { - /* tslint:disable:no-unused-variable */ - let a: Promise; - let o1: Observable = forkJoin(a); - /* tslint:enable:no-unused-variable */ - }); + it('should accept empty lowercase-o observables', () => { + const e1 = forkJoin({ + foo: hot('--a--b--c--d--|'), + bar: hot('(b|)'), + baz: lowerCaseO() + }); + const expected = '|'; + + expectObservable(e1).toBe(expected); + }); + + it('should accept promise', done => { + const e1 = forkJoin({ + foo: of(1), + bar: Promise.resolve(2) + }); + + e1.subscribe({ + next: x => expect(x).to.deep.equal({ foo: 1, bar: 2 }), + complete: done, + }); + }); + + it('should accept array of observables', () => { + const e1 = forkJoin({ + foo: hot('--a--b--c--d--|'), + bar: hot('(b|)'), + baz: hot('--1--2--3--|') + }); + const expected = '--------------(x|)'; + + expectObservable(e1).toBe(expected, {x: { foo: 'd', bar: 'b', baz: '3' }}); + }); + + it('should not emit if any of source observable is empty', () => { + const e1 = forkJoin({ + foo: hot('--a--b--c--d--|'), + bar: hot('(b|)'), + baz: hot('------------------|') + }); + const expected = '------------------|'; + + expectObservable(e1).toBe(expected); + }); + + it('should complete early if any of source is empty and completes before than others', () => { + const e1 = forkJoin({ + foo: hot('--a--b--c--d--|'), + bar: hot('(b|)'), + baz: hot('---------|') + }); + const expected = '---------|'; - type('should return Array when given a single observable', () => { - /* tslint:disable:no-unused-variable */ - let a: Observable; - let o1: Observable = forkJoin(a); - /* tslint:enable:no-unused-variable */ + expectObservable(e1).toBe(expected); + }); + + it('should complete when all sources are empty', () => { + const e1 = forkJoin({ + foo: hot('--------------|'), + bar: hot('---------|') + }); + const expected = '---------|'; + + expectObservable(e1).toBe(expected); + }); + + it('should not complete when only source never completes', () => { + const e1 = forkJoin({ + foo: hot('--------------') + }); + const expected = '-'; + + expectObservable(e1).toBe(expected); + }); + + it('should not complete when one of the sources never completes', () => { + const e1 = forkJoin({ + foo: hot('--------------'), + bar: hot('-a---b--c--|') + }); + const expected = '-'; + + expectObservable(e1).toBe(expected); + }); + + it('should complete when one of the sources never completes but other completes without values', () => { + const e1 = forkJoin({ + foo: hot('--------------'), + bar: hot('------|') + }); + const expected = '------|'; + + expectObservable(e1).toBe(expected); + }); + + // TODO(benlesh): this is the wrong behavior, it should probably throw right away. + it('should have same v5/v6 throwing behavior full argument of null', (done) => { + // It doesn't throw when you pass null + expect(() => forkJoin(null)).not.to.throw(); + + // It doesn't even throw if you subscribe to forkJoin(null). + expect(() => forkJoin(null).subscribe({ + // It sends the error to the subscription. + error: err => done(), + })).not.to.throw(); + }); + + it('should complete if sources object is empty', () => { + const e1 = forkJoin({}); + const expected = '|'; + + expectObservable(e1).toBe(expected); + }); + + it('should raise error when any of source raises error with empty observable', () => { + const e1 = forkJoin({ + lol: hot('------#'), + wut: hot('---------|') + }); + const expected = '------#'; + + expectObservable(e1).toBe(expected); + }); + + it('should raise error when any of source raises error with source that never completes', () => { + const e1 = forkJoin({ + lol: hot('------#'), + wut: hot('----------') + }); + const expected = '------#'; + + expectObservable(e1).toBe(expected); + }); + + it('should raise error when source raises error', () => { + const e1 = forkJoin({ + lol: hot('------#'), + foo: hot('---a-----|') + }); + const expected = '------#'; + + expectObservable(e1).toBe(expected); + }); + + it('should allow unsubscribing early and explicitly', () => { + const e1 = hot('--a--^--b--c---d-| '); + const e1subs = '^ ! '; + const e2 = hot('---e-^---f--g---h-|'); + const e2subs = '^ ! '; + const expected = '---------- '; + const unsub = ' ! '; + + const result = forkJoin({ + e1, e2 + }); + + expectObservable(result, unsub).toBe(expected); + expectSubscriptions(e1.subscriptions).toBe(e1subs); + expectSubscriptions(e2.subscriptions).toBe(e2subs); + }); + + it('should unsubscribe other Observables, when one of them errors', () => { + const e1 = hot('--a--^--b--c---d-| '); + const e1subs = '^ ! '; + const e2 = hot('---e-^---f--g-#'); + const e2subs = '^ ! '; + const expected = '---------# '; + + const result = forkJoin({ + e1, e2 + }); + + expectObservable(result).toBe(expected); + expectSubscriptions(e1.subscriptions).toBe(e1subs); + expectSubscriptions(e2.subscriptions).toBe(e2subs); + }); }); }); diff --git a/spec/observables/from-spec.ts b/spec/observables/from-spec.ts index 8a09e3aa89..495a6717a5 100644 --- a/spec/observables/from-spec.ts +++ b/spec/observables/from-spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { TestScheduler } from 'rxjs/testing'; -import { asyncScheduler, of, from, Observable, asapScheduler, Observer, observable, Subject } from 'rxjs'; -import { first } from 'rxjs/operators'; +import { asyncScheduler, of, from, Observable, asapScheduler, Observer, observable, Subject, EMPTY } from 'rxjs'; +import { first, concatMap, delay } from 'rxjs/operators'; // tslint:disable:no-any declare const asDiagram: any; @@ -18,9 +18,12 @@ function getArguments(...args: T[]) { describe('from', () => { asDiagram('from([10, 20, 30])') ('should create an observable from an array', () => { - const e1 = from([10, 20, 30]) + const e1 = from([10, 20, 30]).pipe( // for the purpose of making a nice diagram, spread out the synchronous emissions - .concatMap((x, i) => of(x).delay(i === 0 ? 0 : 20, rxTestScheduler)); + concatMap((x, i) => of(x).pipe( + delay(i === 0 ? 0 : 20, rxTestScheduler)) + ) + ); const expected = 'x-y-(z|)'; expectObservable(e1).toBe(expected, {x: 10, y: 20, z: 30}); }); @@ -37,7 +40,7 @@ describe('from', () => { type('should return T for InteropObservable objects', () => { /* tslint:disable:no-unused-variable */ const o1: Observable = from([] as number[], asapScheduler); - const o2: Observable<{ a: string }> = from(Observable.empty()); + const o2: Observable<{ a: string }> = from(EMPTY); const o3: Observable<{ b: number }> = from(new Promise<{b: number}>(resolve => resolve())); /* tslint:enable:no-unused-variable */ }); diff --git a/spec/observables/generate-spec.ts b/spec/observables/generate-spec.ts index fc798d19e0..2a92f115ef 100644 --- a/spec/observables/generate-spec.ts +++ b/spec/observables/generate-spec.ts @@ -2,6 +2,7 @@ import { TestScheduler } from 'rxjs/testing'; import { expect } from 'chai'; import { expectObservable } from '../helpers/marble-testing'; import { generate, Subscriber } from 'rxjs'; +import { take } from 'rxjs/operators'; declare function asDiagram(arg: string): Function; declare const rxTestScheduler: TestScheduler; @@ -47,7 +48,9 @@ describe('generate', () => { initialState: 1, iterate: x => x + 1, resultSelector: (x: number) => x.toString() - }).take(5); + }).pipe( + take(5) + ); const expected = '(12345|)'; expectObservable(source).toBe(expected); @@ -92,7 +95,9 @@ describe('generate', () => { const source = generate({ initialState: 1, iterate: x => x * 2 - }).take(3); + }).pipe( + take(3) + ); const expected = '(124|)'; expectObservable(source).toBe(expected, { '1': 1, '2': 2, '4': 4 }); diff --git a/spec/observables/interval-spec.ts b/spec/observables/interval-spec.ts index 83ac93e974..6e93fffc74 100644 --- a/spec/observables/interval-spec.ts +++ b/spec/observables/interval-spec.ts @@ -33,13 +33,13 @@ describe('interval', () => { }); it('should emit when relative interval set to zero', () => { - const e1 = interval(0, rxTestScheduler).take(7); + const e1 = interval(0, rxTestScheduler).pipe(take(7)); const expected = '(0123456|)'; expectObservable(e1).toBe(expected, [0, 1, 2, 3, 4, 5, 6]); }); it('should consider negative interval as zero', () => { - const e1 = interval(-1, rxTestScheduler).take(7); + const e1 = interval(-1, rxTestScheduler).pipe(take(7)); const expected = '(0123456|)'; expectObservable(e1).toBe(expected, [0, 1, 2, 3, 4, 5, 6]); }); @@ -67,7 +67,7 @@ describe('interval', () => { const fakeTimer = sandbox.useFakeTimers(); const period = 10; const events = [0, 1, 2, 3, 4, 5]; - const source = interval(period, asapScheduler).take(6); + const source = interval(period, asapScheduler).pipe(take(6)); source.subscribe({ next(x) { expect(x).to.equal(events.shift()); @@ -94,7 +94,7 @@ describe('interval', () => { const fakeTimer = sandbox.useFakeTimers(); const period = 10; const events = [0, 1, 2, 3, 4, 5]; - const source = interval(period, queueScheduler).take(6); + const source = interval(period, queueScheduler).pipe(take(6)); source.subscribe({ next(x) { expect(x).to.equal(events.shift()); diff --git a/spec/observables/merge-spec.ts b/spec/observables/merge-spec.ts index ccab6bd66a..249b0325a9 100644 --- a/spec/observables/merge-spec.ts +++ b/spec/observables/merge-spec.ts @@ -3,6 +3,7 @@ import { lowerCaseO } from '../helpers/test-helper'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { TestScheduler } from 'rxjs/testing'; import { merge, of, Observable, defer, asyncScheduler } from 'rxjs'; +import { delay } from 'rxjs/operators'; declare const rxTestScheduler: TestScheduler; @@ -249,7 +250,7 @@ describe('merge(...observables, Scheduler, number)', () => { it('should handle scheduler', () => { const e1 = of('a'); - const e2 = of('b').delay(20, rxTestScheduler); + const e2 = of('b').pipe(delay(20, rxTestScheduler)); const expected = 'a-(b|)'; expectObservable(merge(e1, e2, rxTestScheduler)).toBe(expected); diff --git a/spec/observables/of-spec.ts b/spec/observables/of-spec.ts index 740b74ec7b..d62967b4ad 100644 --- a/spec/observables/of-spec.ts +++ b/spec/observables/of-spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { empty, of, Observable } from 'rxjs'; +import { of, Observable } from 'rxjs'; import { expectObservable } from '../helpers/marble-testing'; import { TestScheduler } from 'rxjs/testing'; import { concatMap, delay, concatAll } from 'rxjs/operators'; @@ -33,18 +33,13 @@ describe('of', () => { }); }); - it('should return an empty observable if passed no values', () => { - const obs = of(); - expect(obs).to.equal(empty()); - }); - it('should emit one value', (done: MochaDone) => { let calls = 0; of(42).subscribe((x: number) => { expect(++calls).to.equal(1); expect(x).to.equal(42); - }, (err: any) => { + }, (err: any) => { done(new Error('should not be called')); }, () => { done(); diff --git a/spec/operators/partition-spec.ts b/spec/observables/partition-spec.ts similarity index 83% rename from spec/operators/partition-spec.ts rename to spec/observables/partition-spec.ts index 7b02c52130..000d4fa57c 100644 --- a/spec/operators/partition-spec.ts +++ b/spec/observables/partition-spec.ts @@ -1,14 +1,13 @@ import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; +import { Observable, partition, of } from 'rxjs'; +import { map, mergeMap } from 'rxjs/operators'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; declare function asDiagram(arg: string): Function; -const Observable = Rx.Observable; - /** @test {partition} */ describe('Observable.prototype.partition', () => { - function expectObservableArray(result: Rx.Observable[], expected: string[]) { + function expectObservableArray(result: Observable[], expected: string[]) { for (let idx = 0; idx < result.length; idx++ ) { expectObservable(result[idx]).toBe(expected[idx]); } @@ -21,7 +20,7 @@ describe('Observable.prototype.partition', () => { const expected = ['--1-----3---------5------|', '----2----------4------6--|']; - const result = e1.partition((x: any) => x % 2 === 1); + const result = partition(e1, (x: any) => x % 2 === 1); expectObservableArray(result, expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); @@ -37,7 +36,7 @@ describe('Observable.prototype.partition', () => { return x === 'a'; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -51,7 +50,7 @@ describe('Observable.prototype.partition', () => { return index % 2 === 0; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -65,7 +64,7 @@ describe('Observable.prototype.partition', () => { return x === this.value; } - expectObservableArray(e1.partition(predicate, {value: 'a'}), expected); + expectObservableArray(partition(e1, predicate, {value: 'a'}), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -79,7 +78,7 @@ describe('Observable.prototype.partition', () => { return x === 'a'; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -93,7 +92,7 @@ describe('Observable.prototype.partition', () => { return x === 'a'; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -113,7 +112,7 @@ describe('Observable.prototype.partition', () => { return match; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -127,7 +126,7 @@ describe('Observable.prototype.partition', () => { return x === 'x'; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -141,7 +140,7 @@ describe('Observable.prototype.partition', () => { return x === 'x'; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -155,7 +154,7 @@ describe('Observable.prototype.partition', () => { return x === 'a'; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -169,7 +168,7 @@ describe('Observable.prototype.partition', () => { return x === 'a'; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -183,7 +182,7 @@ describe('Observable.prototype.partition', () => { return x === 'a'; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -197,7 +196,7 @@ describe('Observable.prototype.partition', () => { return x === 'a'; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -211,7 +210,7 @@ describe('Observable.prototype.partition', () => { return x === 'a'; } - expectObservableArray(e1.partition(predicate), expected); + expectObservableArray(partition(e1, predicate), expected); expectSubscriptions(e1.subscriptions).toBe([e1subs, e1subs]); }); @@ -225,7 +224,7 @@ describe('Observable.prototype.partition', () => { function predicate(x: string) { return x === 'a'; } - const result = e1.partition(predicate); + const result = partition(e1, predicate); for (let idx = 0; idx < result.length; idx++ ) { expectObservable(result[idx], unsub).toBe(expected[idx]); @@ -240,11 +239,10 @@ describe('Observable.prototype.partition', () => { '----b--- ']; const unsub = ' ! '; - const result = e1 - .mergeMap((x: string) => Observable.of(x)) - .partition((x: string) => x === 'a') - .map((observable: Rx.Observable) => - observable.mergeMap((x: string) => Observable.of(x))); + const e1Pipe = e1.pipe( + mergeMap((x: string) => of(x)) + ); + const result = partition(e1Pipe, (x: string) => x === 'a'); expectObservable(result[0], unsub).toBe(expected[0]); expectObservable(result[1], unsub).toBe(expected[1]); @@ -254,10 +252,10 @@ describe('Observable.prototype.partition', () => { it('should accept thisArg', () => { const thisArg = {}; - Observable.of(1).partition(function (this: any, value: number) { + partition(of(1), function (this: any, value: number) { expect(this).to.deep.equal(thisArg); return true; }, thisArg) - .forEach((observable: Rx.Observable) => observable.subscribe()); + .forEach((observable: Observable) => observable.subscribe()); }); }); diff --git a/spec/operators/concatMap-spec.ts b/spec/operators/concatMap-spec.ts index 4005a7e059..ee12ed8bf6 100644 --- a/spec/operators/concatMap-spec.ts +++ b/spec/operators/concatMap-spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { Observable, of, from } from 'rxjs'; -import { concatMap, mergeMap } from 'rxjs/operators'; +import { concatMap, mergeMap, map } from 'rxjs/operators'; declare function asDiagram(arg: string): Function; @@ -15,7 +15,11 @@ describe('Observable.prototype.concatMap', () => { const expected = '--x-x-x-y-y-yz-z-z-|'; const values = {x: 10, y: 30, z: 50}; - const result = e1.concatMap(x => e2.map(i => i * parseInt(x))); + const result = e1.pipe( + concatMap(x => e2.pipe( + map(i => i * parseInt(x)) + )) + ); expectObservable(result).toBe(expected, values); expectSubscriptions(e1.subscriptions).toBe(e1subs); @@ -559,10 +563,13 @@ describe('Observable.prototype.concatMap', () => { const expected = '---2--3--4--5----6-----2--3# '; const observableLookup = { a: a, b: b, c: c, d: d, e: e, f: f, g: g }; - const result = e1.concatMap((value) => { - if (value === 'e') { throw 'error'; } - return observableLookup[value]; - }); + const result = e1.pipe( + concatMap((value) => { + if (value === 'e') { throw 'error'; } + return observableLookup[value]; + } + ) + ); expectObservable(result).toBe(expected); expectSubscriptions(a.subscriptions).toBe(asubs); @@ -588,7 +595,7 @@ describe('Observable.prototype.concatMap', () => { const e1subs = '^ !'; const expected = '(22)--(4444)---(333)----(22)----|'; - const result = e1.concatMap((value) => arrayRepeat(value, +value)); + const result = e1.pipe(concatMap((value) => arrayRepeat(value, +value))); expectObservable(result).toBe(expected); expectSubscriptions(e1.subscriptions).toBe(e1subs); @@ -600,7 +607,7 @@ describe('Observable.prototype.concatMap', () => { const unsub = ' ! '; const expected = '(22)--(4444)-- '; - const result = e1.concatMap((value) => arrayRepeat(value, +value)); + const result = e1.pipe(concatMap((value) => arrayRepeat(value, +value))); expectObservable(result, unsub).toBe(expected); expectSubscriptions(e1.subscriptions).toBe(e1subs); @@ -612,23 +619,23 @@ describe('Observable.prototype.concatMap', () => { const expected = '(22)--(4444)---# '; let invoked = 0; - const result = e1.concatMap((value) => { + const result = e1.pipe(concatMap((value) => { invoked++; if (invoked === 3) { throw 'error'; } return arrayRepeat(value, +value); - }); + })); expectObservable(result).toBe(expected); expectSubscriptions(e1.subscriptions).toBe(e1subs); }); it('should map values to constant resolved promises and concatenate', (done: MochaDone) => { const source = from([4, 3, 2, 1]); - const project = (value: number) => Observable.from(Promise.resolve(42)); + const project = (value: number) => from(Promise.resolve(42)); const results: number[] = []; - source.concatMap(project).subscribe( + source.pipe(concatMap(project)).subscribe( (x) => { results.push(x); }, (err) => { @@ -641,9 +648,9 @@ describe('Observable.prototype.concatMap', () => { it('should map values to constant rejected promises and concatenate', (done) => { const source = from([4, 3, 2, 1]); - const project = (value: any) => Observable.from(Promise.reject(42)); + const project = (value: any) => from(Promise.reject(42)); - source.concatMap(project).subscribe( + source.pipe(concatMap(project)).subscribe( (x) => { done(new Error('Subscriber next handler not supposed to be called.')); }, (err) => { @@ -656,10 +663,10 @@ describe('Observable.prototype.concatMap', () => { it('should map values to resolved promises and concatenate', (done) => { const source = from([4, 3, 2, 1]); - const project = (value: number, index: number) => Observable.from(Promise.resolve(value + index)); + const project = (value: number, index: number) => from(Promise.resolve(value + index)); const results: number[] = []; - source.concatMap(project).subscribe( + source.pipe(concatMap(project)).subscribe( (x) => { results.push(x); }, (err) => { @@ -672,9 +679,9 @@ describe('Observable.prototype.concatMap', () => { it('should map values to rejected promises and concatenate', (done) => { const source = from([4, 3, 2, 1]); - const project = (value: number, index: number) => Observable.from(Promise.reject('' + value + '-' + index)); + const project = (value: number, index: number) => from(Promise.reject('' + value + '-' + index)); - source.concatMap(project).subscribe( + source.pipe(concatMap(project)).subscribe( (x) => { done(new Error('Subscriber next handler not supposed to be called.')); }, (err) => { diff --git a/spec/operators/defaultIfEmpty-spec.ts b/spec/operators/defaultIfEmpty-spec.ts index 1104fd80ff..a3f4f5b625 100644 --- a/spec/operators/defaultIfEmpty-spec.ts +++ b/spec/operators/defaultIfEmpty-spec.ts @@ -1,17 +1,17 @@ -import * as Rx from 'rxjs/Rx'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; +import { of } from 'rxjs'; +import { defaultIfEmpty, mergeMap } from 'rxjs/operators'; declare function asDiagram(arg: string): Function; -const Observable = Rx.Observable; - /** @test {defaultIfEmpty} */ describe('Observable.prototype.defaultIfEmpty', () => { asDiagram('defaultIfEmpty(42)')('should return the Observable if not empty with a default value', () => { const e1 = hot('--------|'); const expected = '--------(x|)'; - expectObservable(e1.defaultIfEmpty(42)).toBe(expected, { x: 42 }); + // TODO: Fix `defaultIfEmpty` typings + expectObservable(e1.pipe(defaultIfEmpty(42) as any)).toBe(expected, { x: 42 }); }); it('should return the argument if Observable is empty', () => { @@ -19,7 +19,7 @@ describe('Observable.prototype.defaultIfEmpty', () => { const e1subs = '(^!)'; const expected = '(x|)'; - expectObservable(e1.defaultIfEmpty('x')).toBe(expected); + expectObservable(e1.pipe(defaultIfEmpty('x'))).toBe(expected); expectSubscriptions(e1.subscriptions).toBe(e1subs); }); @@ -28,7 +28,7 @@ describe('Observable.prototype.defaultIfEmpty', () => { const e1subs = '(^!)'; const expected = '(x|)'; - expectObservable(e1.defaultIfEmpty()).toBe(expected, { x: null }); + expectObservable(e1.pipe(defaultIfEmpty())).toBe(expected, { x: null }); expectSubscriptions(e1.subscriptions).toBe(e1subs); }); @@ -37,7 +37,7 @@ describe('Observable.prototype.defaultIfEmpty', () => { const e1subs = '^ !'; const expected = '--a--b--|'; - expectObservable(e1.defaultIfEmpty('x')).toBe(expected); + expectObservable(e1.pipe(defaultIfEmpty('x'))).toBe(expected); expectSubscriptions(e1.subscriptions).toBe(e1subs); }); @@ -46,7 +46,7 @@ describe('Observable.prototype.defaultIfEmpty', () => { const e1subs = '^ !'; const expected = '--a--b--|'; - expectObservable(e1.defaultIfEmpty()).toBe(expected); + expectObservable(e1.pipe(defaultIfEmpty())).toBe(expected); expectSubscriptions(e1.subscriptions).toBe(e1subs); }); @@ -56,7 +56,7 @@ describe('Observable.prototype.defaultIfEmpty', () => { const expected = '--a-- '; const unsub = ' ! '; - const result = e1.defaultIfEmpty('x'); + const result = e1.pipe(defaultIfEmpty('x')); expectObservable(result, unsub).toBe(expected); expectSubscriptions(e1.subscriptions).toBe(e1subs); @@ -68,10 +68,11 @@ describe('Observable.prototype.defaultIfEmpty', () => { const expected = '--a-- '; const unsub = ' ! '; - const result = e1 - .mergeMap((x: any) => Observable.of(x)) - .defaultIfEmpty('x') - .mergeMap((x: any) => Observable.of(x)); + const result = e1.pipe( + mergeMap(x => of(x)), + defaultIfEmpty('x'), + mergeMap(x => of(x)), + ); expectObservable(result, unsub).toBe(expected); expectSubscriptions(e1.subscriptions).toBe(e1subs); @@ -82,7 +83,7 @@ describe('Observable.prototype.defaultIfEmpty', () => { const e1subs = '(^!)'; const expected = '#'; - expectObservable(e1.defaultIfEmpty('x')).toBe(expected); + expectObservable(e1.pipe(defaultIfEmpty('x'))).toBe(expected); expectSubscriptions(e1.subscriptions).toBe(e1subs); }); }); diff --git a/spec/operators/elementAt-spec.ts b/spec/operators/elementAt-spec.ts index edc3ae7c53..93228b40d5 100644 --- a/spec/operators/elementAt-spec.ts +++ b/spec/operators/elementAt-spec.ts @@ -99,7 +99,7 @@ describe('elementAt operator', () => { }); it('should throw if index is smaller than zero', () => { - expect(() => { (range(0, 10)).elementAt(-1); }) + expect(() => { range(0, 10).pipe(elementAt(-1)); }) .to.throw(ArgumentOutOfRangeError); }); @@ -119,7 +119,7 @@ describe('elementAt operator', () => { const expected = '-----(x|)'; const defaultValue = '42'; - expectObservable((source).pipe(elementAt(3, defaultValue))).toBe(expected, { x: defaultValue }); + expectObservable(source.pipe(elementAt(3, defaultValue))).toBe(expected, { x: defaultValue }); expectSubscriptions(source.subscriptions).toBe(subs); }); }); diff --git a/spec/operators/exhaustMap-spec.ts b/spec/operators/exhaustMap-spec.ts index 2819e57b32..2fbcfa69da 100644 --- a/spec/operators/exhaustMap-spec.ts +++ b/spec/operators/exhaustMap-spec.ts @@ -1,6 +1,6 @@ import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { concat, defer, Observable, of, from } from 'rxjs'; -import { exhaustMap, mergeMap, takeWhile } from 'rxjs/operators'; +import { exhaustMap, mergeMap, takeWhile, map } from 'rxjs/operators'; import { expect } from 'chai'; declare function asDiagram(arg: string): Function; @@ -15,7 +15,7 @@ describe('exhaustMap', () => { const expected = '--x-x-x-y-y-y------|'; const values = {x: 10, y: 30, z: 50}; - const result = e1.pipe(exhaustMap(x => e2.map(i => i * +x))); + const result = e1.pipe(exhaustMap(x => e2.pipe(map(i => i * +x)))); expectObservable(result).toBe(expected, values); expectSubscriptions(e1.subscriptions).toBe(e1subs); diff --git a/spec/operators/expand-spec.ts b/spec/operators/expand-spec.ts index 1d0d38b42c..813c905b8f 100644 --- a/spec/operators/expand-spec.ts +++ b/spec/operators/expand-spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { expand, mergeMap } from 'rxjs/operators'; +import { expand, mergeMap, map } from 'rxjs/operators'; import { TestScheduler } from 'rxjs/testing'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { Subscribable, EMPTY, Observable, of, Observer } from 'rxjs'; @@ -19,7 +19,7 @@ describe('expand operator', () => { const expected = '--a-b-c-d|'; const values = {a: 1, b: 2, c: 4, d: 8}; - const result = e1.pipe(expand(x => x === 8 ? EMPTY : e2.map(c => c * x))); + const result = e1.pipe(expand(x => x === 8 ? EMPTY : e2.pipe(map(c => c * x)))); expectObservable(result).toBe(expected, values); expectSubscriptions(e1.subscriptions).toBe(e1subs); @@ -32,7 +32,7 @@ describe('expand operator', () => { const expected = '--a-b-c-d|'; const values = {a: 1, b: 2, c: 4, d: 8}; - const result = e1.pipe(expand(x => x === 8 ? EMPTY : e2.map(c => c * x), Number.POSITIVE_INFINITY, rxTestScheduler)); + const result = e1.pipe(expand(x => x === 8 ? EMPTY : e2.pipe(map(c => c * x)), Number.POSITIVE_INFINITY, rxTestScheduler)); expectObservable(result).toBe(expected, values); expectSubscriptions(e1.subscriptions).toBe(e1subs); diff --git a/spec/operators/first-spec.ts b/spec/operators/first-spec.ts index 1a64c12b32..dea1931cb6 100644 --- a/spec/operators/first-spec.ts +++ b/spec/operators/first-spec.ts @@ -200,27 +200,27 @@ describe('Observable.prototype.first', () => { const isBaz = (x: any): x is Baz => x && (x as Baz).baz !== undefined; const foo: Foo = new Foo(); - Observable.of(foo).pipe(first()) + of(foo).pipe(first()) .subscribe(x => x.baz); // x is Foo - Observable.of(foo).pipe(first(foo => foo.bar === 'name')) + of(foo).pipe(first(foo => foo.bar === 'name')) .subscribe(x => x.baz); // x is still Foo - Observable.of(foo).pipe(first(isBar)) + of(foo).pipe(first(isBar)) .subscribe(x => x.bar); // x is Bar! const foobar: Bar = new Foo(); // type is the interface, not the class - Observable.of(foobar).pipe(first()) + of(foobar).pipe(first()) .subscribe(x => x.bar); // x is Bar - Observable.of(foobar).pipe(first(foobar => foobar.bar === 'name')) + of(foobar).pipe(first(foobar => foobar.bar === 'name')) .subscribe(x => x.bar); // x is still Bar - Observable.of(foobar).pipe(first(isBaz)) + of(foobar).pipe(first(isBaz)) .subscribe(x => x.baz); // x is Baz! const barish = { bar: 'quack', baz: 42 }; // type can quack like a Bar - Observable.of(barish).pipe(first()) + of(barish).pipe(first()) .subscribe(x => x.baz); // x is still { bar: string; baz: number; } - Observable.of(barish).pipe(first(x => x.bar === 'quack')) + of(barish).pipe(first(x => x.bar === 'quack')) .subscribe(x => x.bar); // x is still { bar: string; baz: number; } - Observable.of(barish).pipe(first(isBar)) + of(barish).pipe(first(isBar)) .subscribe(x => x.bar); // x is Bar! } diff --git a/spec/operators/forkJoin-spec.ts b/spec/operators/forkJoin-spec.ts index c2aab4ec29..5f9b2362b2 100644 --- a/spec/operators/forkJoin-spec.ts +++ b/spec/operators/forkJoin-spec.ts @@ -1,12 +1,8 @@ -import * as Rx from 'rxjs/Rx'; -import { expect } from 'chai'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { forkJoin } from 'rxjs'; declare function asDiagram(arg: string): Function; -const Observable = Rx.Observable; - /** @test {forkJoin} */ describe('Observable.prototype.forkJoin', () => { asDiagram('forkJoin')('should emit the last emitted value from each observable', () => { @@ -42,5 +38,4 @@ describe('Observable.prototype.forkJoin', () => { const result = forkJoin(e1, e2); expectObservable(result).toBe(expected); }); - -}); \ No newline at end of file +}); diff --git a/spec/operators/groupBy-spec.ts b/spec/operators/groupBy-spec.ts index da1777bbd3..8dfa74bd2b 100644 --- a/spec/operators/groupBy-spec.ts +++ b/spec/operators/groupBy-spec.ts @@ -365,9 +365,11 @@ describe('groupBy operator', () => { const expected = '--(a|)'; const source = e1.pipe( - groupBy((val: string) => val.toLowerCase().trim()), + groupBy(val => val.toLowerCase().trim()), take(1), - mergeMap((group: any) => group.take(1)) + mergeMap(group => group.pipe( + take(1) + )) ); expectObservable(source).toBe(expected, values); diff --git a/spec/operators/last-spec.ts b/spec/operators/last-spec.ts index 2642b2326b..1cb84e72b9 100644 --- a/spec/operators/last-spec.ts +++ b/spec/operators/last-spec.ts @@ -128,9 +128,9 @@ describe('Observable.prototype.last', () => { const foo: Foo = new Foo(); of(foo).pipe(last()) .subscribe(x => x.baz); // x is Foo - of(foo).last(foo => foo.bar === 'name') + of(foo).pipe(last(foo => foo.bar === 'name')) .subscribe(x => x.baz); // x is still Foo - of(foo).last(isBar) + of(foo).pipe(last(isBar)) .subscribe(x => x.bar); // x is Bar! const foobar: Bar = new Foo(); // type is the interface, not the class diff --git a/spec/operators/let-spec.ts b/spec/operators/let-spec.ts deleted file mode 100644 index aff139c936..0000000000 --- a/spec/operators/let-spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; - -/** @test {let} */ -describe('Observable.prototype.let', () => { - it('should be able to compose with let', (done: MochaDone) => { - const expected = ['aa', 'bb']; - let i = 0; - - const foo = (observable: Rx.Observable) => observable.map((x: string) => x + x); - - Rx.Observable - .from(['a', 'b']) - .let(foo) - .subscribe(function (x) { - expect(x).to.equal(expected[i++]); - }, (x) => { - done(new Error('should not be called')); - }, () => { - done(); - }); - }); -}); diff --git a/spec/operators/mergeMap-spec.ts b/spec/operators/mergeMap-spec.ts index cb21aff151..d95fe8010b 100644 --- a/spec/operators/mergeMap-spec.ts +++ b/spec/operators/mergeMap-spec.ts @@ -16,7 +16,9 @@ describe('mergeMap', () => { const expected = '--x-x-x-y-yzyz-z---|'; const values = {x: 10, y: 30, z: 50}; - const result = e1.pipe(mergeMap(x => e2.map(i => i * +x))); + const result = e1.pipe(mergeMap(x => e2.pipe( + map(i => i * +x) + ))); expectObservable(result).toBe(expected, values); expectSubscriptions(e1.subscriptions).toBe(e1subs); diff --git a/spec/operators/mergeScan-spec.ts b/spec/operators/mergeScan-spec.ts index 3b88ba8879..d03c2c791d 100644 --- a/spec/operators/mergeScan-spec.ts +++ b/spec/operators/mergeScan-spec.ts @@ -1,7 +1,7 @@ import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { TestScheduler } from 'rxjs/testing'; import { of, defer, EMPTY, NEVER, concat, throwError } from 'rxjs'; -import { mergeScan, delay, mergeMap, takeWhile } from 'rxjs/operators'; +import { mergeScan, delay, mergeMap, takeWhile, startWith } from 'rxjs/operators'; import { expect } from 'chai'; declare const rxTestScheduler: TestScheduler; @@ -59,7 +59,7 @@ describe('mergeScan', () => { }; const source = e1.pipe(mergeScan((acc, x) => - of(acc.concat(x)).delay(20, rxTestScheduler), [])); + of(acc.concat(x)).pipe(delay(20, rxTestScheduler)), [])); expectObservable(source).toBe(expected, values); expectSubscriptions(e1.subscriptions).toBe(e1subs); @@ -80,7 +80,7 @@ describe('mergeScan', () => { }; const source = e1.pipe(mergeScan((acc, x) => - of(acc.concat(x)).delay(50, rxTestScheduler), [])); + of(acc.concat(x)).pipe(delay(50, rxTestScheduler)), [])); expectObservable(source).toBe(expected, values); expectSubscriptions(e1.subscriptions).toBe(e1subs); @@ -101,7 +101,7 @@ describe('mergeScan', () => { }; const source = e1.pipe(mergeScan((acc, x) => - of(acc.concat(x)).delay(50, rxTestScheduler), [])); + of(acc.concat(x)).pipe(delay(50, rxTestScheduler)), [])); expectObservable(source, e1subs).toBe(expected, values); expectSubscriptions(e1.subscriptions).toBe(e1subs); @@ -295,7 +295,7 @@ describe('mergeScan', () => { let index = 0; const source = e1.pipe(mergeScan((acc, x) => { const value = inner[index++]; - return value.startWith(acc); + return value.pipe(startWith(acc)); }, 'x', 1)); expectObservable(source).toBe(expected); @@ -323,7 +323,7 @@ describe('mergeScan', () => { const expected = '-----------------------(x|)'; const source = e1.pipe( - mergeScan((acc, x) => EMPTY.delay(50, rxTestScheduler), ['1']) + mergeScan((acc, x) => EMPTY.pipe(delay(50, rxTestScheduler)), ['1']) ); expectObservable(source).toBe(expected, {x: ['1']}); @@ -349,7 +349,7 @@ describe('mergeScan', () => { let index = 0; const source = e1.pipe(mergeScan((acc, x) => { const value = inner[index++]; - return value.startWith(acc); + return value.pipe(startWith(acc)); }, 'x', 1)); expectObservable(source).toBe(expected); @@ -379,7 +379,7 @@ describe('mergeScan', () => { let index = 0; const source = e1.pipe(mergeScan((acc, x) => { const value = inner[index++]; - return value.startWith(acc); + return value.pipe(startWith(acc)); }, 'x', 2)); expectObservable(source).toBe(expected); @@ -409,7 +409,9 @@ describe('mergeScan', () => { let index = 0; const source = e1.pipe(mergeScan((acc, x) => { const value = inner[index++]; - return value.startWith(acc); + return value.pipe( + startWith(acc) + ); }, 'x', 2)); expectObservable(source).toBe(expected); diff --git a/spec/operators/publish-spec.ts b/spec/operators/publish-spec.ts index 0e5480dc12..08868cdd0f 100644 --- a/spec/operators/publish-spec.ts +++ b/spec/operators/publish-spec.ts @@ -42,11 +42,11 @@ describe('publish operator', () => { const source = cold('-1-2-3----4-|'); const sourceSubs = '^ !'; const published = source.pipe(publish()) as ConnectableObservable; - const subscriber1 = hot('a| ').mergeMapTo(published); + const subscriber1 = hot('a| ').pipe(mergeMapTo(published)); const expected1 = '-1-2-3----4-|'; - const subscriber2 = hot(' b| ').mergeMapTo(published); + const subscriber2 = hot(' b| ').pipe(mergeMapTo(published)); const expected2 = ' -3----4-|'; - const subscriber3 = hot(' c| ').mergeMapTo(published); + const subscriber3 = hot(' c| ').pipe(mergeMapTo(published)); const expected3 = ' --4-|'; expectObservable(subscriber1).toBe(expected1); @@ -175,7 +175,7 @@ describe('publish operator', () => { it('should disconnect when last subscriber unsubscribes', () => { const source = cold( '-1-2-3----4-|'); const sourceSubs = ' ^ ! '; - const replayed = source.publish().refCount(); + const replayed = source.pipe(publish(), refCount()); const subscriber1 = hot(' a| ').pipe(mergeMapTo(replayed)); const unsub1 = ' ! '; const expected1 = ' -1-2-3-- '; @@ -373,7 +373,7 @@ describe('publish operator', () => { type('should infer the type for the pipeable operator with a selector', () => { /* tslint:disable:no-unused-variable */ const source = of(1, 2, 3); - const result: Observable = source.pipe(publish(s => s.map(x => x))); + const result: Observable = source.pipe(publish(s => s.pipe(map(x => x)))); /* tslint:enable:no-unused-variable */ }); diff --git a/spec/operators/refCount-spec.ts b/spec/operators/refCount-spec.ts index cca1469563..58848088a1 100644 --- a/spec/operators/refCount-spec.ts +++ b/spec/operators/refCount-spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; -import { refCount, publish, publishReplay } from 'rxjs/operators'; +import { refCount, publish, publishReplay, first } from 'rxjs/operators'; import { NEVER, noop, Observable, Observer, Subject, ConnectableObservable } from 'rxjs'; declare function asDiagram(arg: string): Function; @@ -108,7 +108,7 @@ describe('refCount', () => { subject.next('the number one'); - refCounted.first().subscribe().unsubscribe(); + refCounted.pipe(first()).subscribe().unsubscribe(); subject.next('the number two'); diff --git a/spec/operators/share-spec.ts b/spec/operators/share-spec.ts index 995616629c..a239634ed8 100644 --- a/spec/operators/share-spec.ts +++ b/spec/operators/share-spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; -import { share, retry, mergeMapTo, mergeMap, tap } from 'rxjs/operators'; +import { share, retry, mergeMapTo, mergeMap, tap, repeat } from 'rxjs/operators'; import { Observable, EMPTY, NEVER, of } from 'rxjs'; declare function asDiagram(arg: string): Function; @@ -220,11 +220,11 @@ describe('share operator', () => { ' (^!)']; expectObservable(hot(subscribe1).pipe(tap(() => { - expectObservable(shared.retry(1)).toBe(expected1); + expectObservable(shared.pipe(retry(1))).toBe(expected1); }))).toBe(subscribe1); expectObservable(hot(subscribe2).pipe(tap(() => { - expectObservable(shared.retry(1)).toBe(expected2); + expectObservable(shared.pipe(retry(1))).toBe(expected2); }))).toBe(subscribe2); expectSubscriptions(source.subscriptions).toBe(sourceSubs); @@ -243,11 +243,11 @@ describe('share operator', () => { ' (^!)']; expectObservable(hot(subscribe1).pipe(tap(() => { - expectObservable(shared.repeat(2)).toBe(expected1); + expectObservable(shared.pipe(repeat(2))).toBe(expected1); }))).toBe(subscribe1); expectObservable(hot(subscribe2).pipe(tap(() => { - expectObservable(shared.repeat(2)).toBe(expected2); + expectObservable(shared.pipe(repeat(2))).toBe(expected2); }))).toBe(subscribe2); expectSubscriptions(source.subscriptions).toBe(sourceSubs); @@ -265,11 +265,11 @@ describe('share operator', () => { const expected2 = ' -3----4--1-2-3----4--1-2-3----4-#'; expectObservable(hot(subscribe1).pipe(tap(() => { - expectObservable(shared.retry(2)).toBe(expected1); + expectObservable(shared.pipe(retry(2))).toBe(expected1); }))).toBe(subscribe1); expectObservable(hot(subscribe2).pipe(tap(() => { - expectObservable(shared.retry(2)).toBe(expected2); + expectObservable(shared.pipe(retry(2))).toBe(expected2); }))).toBe(subscribe2); expectSubscriptions(source.subscriptions).toBe(sourceSubs); @@ -287,11 +287,11 @@ describe('share operator', () => { const expected2 = ' -3----4--1-2-3----4--1-2-3----4-|'; expectObservable(hot(subscribe1).pipe(tap(() => { - expectObservable(shared.repeat(3)).toBe(expected1); + expectObservable(shared.pipe(repeat(3))).toBe(expected1); }))).toBe(subscribe1); expectObservable(hot(subscribe2).pipe(tap(() => { - expectObservable(shared.repeat(3)).toBe(expected2); + expectObservable(shared.pipe(repeat(3))).toBe(expected2); }))).toBe(subscribe2); expectSubscriptions(source.subscriptions).toBe(sourceSubs); @@ -305,7 +305,7 @@ describe('share operator', () => { }); it('should not change the output of the observable when empty', () => { - const e1 = Observable.empty(); + const e1 = EMPTY; const expected = '|'; expectObservable(e1.pipe(share())).toBe(expected); diff --git a/spec/operators/switch-spec.ts b/spec/operators/switch-spec.ts index b6bf4c4cab..cbe9b3a10d 100644 --- a/spec/operators/switch-spec.ts +++ b/spec/operators/switch-spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; import { Observable, of, NEVER, queueScheduler, Subject } from 'rxjs'; -import { map, switchAll } from 'rxjs/operators'; +import { map, switchAll, mergeMap } from 'rxjs/operators'; declare function asDiagram(arg: string): Function; declare const type: Function; @@ -32,15 +32,15 @@ describe('switchAll', () => { it('should unsub inner observables', () => { const unsubbed: string[] = []; - of('a', 'b').map((x) => - new Observable((subscriber) => { + of('a', 'b').pipe( + map((x) => new Observable((subscriber) => { subscriber.complete(); return () => { unsubbed.push(x); }; - })) - .pipe(switchAll()) - .subscribe(); + })), + switchAll() + ).subscribe(); expect(unsubbed).to.deep.equal(['a', 'b']); }); @@ -89,10 +89,11 @@ describe('switchAll', () => { const expected = '--------a---b---- '; const unsub = ' ! '; - const result = e1 - .mergeMap((x) => of(x)) - .pipe(switchAll()) - .mergeMap((x) => of(x)); + const result = e1.pipe( + mergeMap((x) => of(x)), + switchAll(), + mergeMap((x) => of(x)), + ); expectObservable(result, unsub).toBe(expected); expectSubscriptions(x.subscriptions).toBe(xsubs); @@ -263,54 +264,4 @@ describe('switchAll', () => { ).to.equal(2); sub.unsubscribe(); }); - - type(() => { - /* tslint:disable:no-unused-variable */ - const source1 = of(1, 2, 3); - const source2 = [1, 2, 3]; - const source3 = new Promise(d => d(1)); - - let result: Observable = Observable - .of(source1, source2, source3) - .pipe(switchAll()); - /* tslint:enable:no-unused-variable */ - }); - - type(() => { - /* tslint:disable:no-unused-variable */ - const source1 = of(1, 2, 3); - const source2 = [1, 2, 3]; - const source3 = new Promise(d => d(1)); - - let result: Observable = Observable - .of(source1, source2, source3) - .pipe(switchAll()); - /* tslint:enable:no-unused-variable */ - }); - - type(() => { - // coerce type to a specific type - /* tslint:disable:no-unused-variable */ - const source1 = of(1, 2, 3); - const source2 = [1, 2, 3]; - const source3 = new Promise(d => d(1)); - - let result: Observable = Observable - .of(source1 as any, source2 as any, source3 as any) - .pipe(switchAll()); - /* tslint:enable:no-unused-variable */ - }); - - type(() => { - // coerce type to a specific type - /* tslint:disable:no-unused-variable */ - const source1 = of(1, 2, 3); - const source2 = [1, 2, 3]; - const source3 = new Promise(d => d(1)); - - let result: Observable = Observable - .of(source1 as any, source2 as any, source3 as any) - .switch(); - /* tslint:enable:no-unused-variable */ - }); }); diff --git a/spec/operators/throwIfEmpty-spec.ts b/spec/operators/throwIfEmpty-spec.ts index 68ed8bec02..44304d7847 100644 --- a/spec/operators/throwIfEmpty-spec.ts +++ b/spec/operators/throwIfEmpty-spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; -import { EMPTY, of, EmptyError } from 'rxjs'; -import { throwIfEmpty } from 'rxjs/operators'; +import { EMPTY, of, EmptyError, defer, throwError } from 'rxjs'; +import { throwIfEmpty, mergeMap, retry } from 'rxjs/operators'; declare function asDiagram(arg: string): Function; @@ -77,6 +77,39 @@ describe('throwIfEmpty', () => { ).toBe(expected, undefined, new Error('test')); expectSubscriptions(source.subscriptions).toBe([sub1]); }); + + it('should throw if empty after retry', () => { + const error = new Error('So empty inside'); + let thrown: any; + let sourceIsEmpty = false; + + const source = defer(() => { + if (sourceIsEmpty) { + return EMPTY; + } + sourceIsEmpty = true; + return of(1, 2); + }); + + source.pipe( + throwIfEmpty(() => error), + mergeMap(value => { + if (value > 1) { + return throwError(new Error()); + } + + return of(value); + }), + retry(1) + ).subscribe({ + error(err) { + thrown = err; + } + }); + + expect(thrown).to.equal(error); + + }); }); describe('without errorFactory', () => { @@ -139,5 +172,37 @@ describe('throwIfEmpty', () => { ).toBe(expected, undefined, new EmptyError()); expectSubscriptions(source.subscriptions).toBe([sub1]); }); + + it('should throw if empty after retry', () => { + let thrown: any; + let sourceIsEmpty = false; + + const source = defer(() => { + if (sourceIsEmpty) { + return EMPTY; + } + sourceIsEmpty = true; + return of(1, 2); + }); + + source.pipe( + throwIfEmpty(), + mergeMap(value => { + if (value > 1) { + return throwError(new Error()); + } + + return of(value); + }), + retry(1) + ).subscribe({ + error(err) { + thrown = err; + } + }); + + expect(thrown).to.be.instanceof(EmptyError); + + }); }); }); diff --git a/spec/operators/windowToggle-spec.ts b/spec/operators/windowToggle-spec.ts index 2d9ce9fb3d..4a79752397 100644 --- a/spec/operators/windowToggle-spec.ts +++ b/spec/operators/windowToggle-spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { hot, cold, expectObservable, expectSubscriptions, time } from '../helpers/marble-testing'; -import { Observable, NEVER, of, ObjectUnsubscribedError } from 'rxjs'; -import { windowToggle, tap } from 'rxjs/operators'; +import { Observable, NEVER, of, ObjectUnsubscribedError, EMPTY } from 'rxjs'; +import { windowToggle, tap, mergeMap } from 'rxjs/operators'; import { TestScheduler } from 'rxjs/testing'; declare const rxTestScheduler: TestScheduler; @@ -189,10 +189,11 @@ describe('windowToggle', () => { const values = { x, y }; let i = 0; - const result = e1 - .mergeMap((x: string) => of(x)) - .pipe(windowToggle(e2, () => close[i++])) - .mergeMap(x => of(x)); + const result = e1.pipe( + mergeMap(x => of(x)), + windowToggle(e2, () => close[i++]), + mergeMap(x => of(x)), + ); expectObservable(result, unsub).toBe(expected, values); expectSubscriptions(e1.subscriptions).toBe(e1subs); @@ -418,7 +419,7 @@ describe('windowToggle', () => { const e1subs = '^ !'; const e2 = cold( '---o---------------o-----------| '); const e2subs = '^ ! '; - const e3 = Observable.empty(); + const e3 = EMPTY; const expected = '---x---------------y---------------|'; const x = cold( '|'); const y = cold( '|'); diff --git a/spec/root-module-spec.ts b/spec/root-module-spec.ts deleted file mode 100644 index d5449c3d57..0000000000 --- a/spec/root-module-spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; - -describe('Root Module', () => { - it('should contain exports from commonjs modules', () => { - expect(Rx.Observable).to.be.a('function'); - }); -}); diff --git a/spec/scheduled/scheduled-spec.ts b/spec/scheduled/scheduled-spec.ts new file mode 100644 index 0000000000..89cfd859fe --- /dev/null +++ b/spec/scheduled/scheduled-spec.ts @@ -0,0 +1,63 @@ +import { scheduled, of } from 'rxjs'; +import { TestScheduler } from 'rxjs/testing'; +import { lowerCaseO } from '../helpers/test-helper'; +import { observableMatcher } from '../helpers/observableMatcher'; +import { expect } from 'chai'; + +describe('scheduled', () => { + let testScheduler: TestScheduler; + + beforeEach(() => { + testScheduler = new TestScheduler(observableMatcher); + }); + + it('should schedule a sync observable', () => { + const input = of('a', 'b', 'c'); + testScheduler.run(({ expectObservable }) => { + expectObservable(scheduled(input, testScheduler)).toBe('(abc|)'); + }); + }); + + it('should schedule an array', () => { + const input = ['a', 'b', 'c']; + testScheduler.run(({ expectObservable }) => { + expectObservable(scheduled(input, testScheduler)).toBe('(abc|)'); + }); + }); + + it('should schedule an iterable', () => { + const input = 'abc'; // strings are iterables + testScheduler.run(({ expectObservable }) => { + expectObservable(scheduled(input, testScheduler)).toBe('(abc|)'); + }); + }); + + it('should schedule an observable-like', () => { + const input = lowerCaseO('a', 'b', 'c'); // strings are iterables + testScheduler.run(({ expectObservable }) => { + expectObservable(scheduled(input, testScheduler)).toBe('(abc|)'); + }); + }); + + it('should schedule a promise', done => { + const results: any[] = []; + const input = Promise.resolve('x'); // strings are iterables + scheduled(input, testScheduler).subscribe({ + next(value) { results.push(value); }, + complete() { results.push('done'); }, + }); + + expect(results).to.deep.equal([]); + + // Promises force async, so we can't schedule synchronously, no matter what. + testScheduler.flush(); + expect(results).to.deep.equal([]); + + Promise.resolve().then(() => { + // NOW it should work, as the other promise should have resolved. + testScheduler.flush(); + expect(results).to.deep.equal(['x', 'done']); + done(); + }); + }); +}); diff --git a/spec/subjects/AsyncSubject-spec.ts b/spec/subjects/AsyncSubject-spec.ts index 7035bc28e4..20b65d7f8b 100644 --- a/spec/subjects/AsyncSubject-spec.ts +++ b/spec/subjects/AsyncSubject-spec.ts @@ -1,9 +1,7 @@ import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; +import { AsyncSubject, Observer } from 'rxjs'; -const AsyncSubject = Rx.AsyncSubject; - -class TestObserver implements Rx.Observer { +class TestObserver implements Observer { results: (number | string)[] = []; next(value: number): void { diff --git a/spec/subjects/BehaviorSubject-spec.ts b/spec/subjects/BehaviorSubject-spec.ts index 21e7cb4e72..fb02c7cb54 100644 --- a/spec/subjects/BehaviorSubject-spec.ts +++ b/spec/subjects/BehaviorSubject-spec.ts @@ -1,16 +1,13 @@ import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; import { hot, expectObservable } from '../helpers/marble-testing'; - -const BehaviorSubject = Rx.BehaviorSubject; -const Observable = Rx.Observable; -const ObjectUnsubscribedError = Rx.ObjectUnsubscribedError; +import { BehaviorSubject, Subject, ObjectUnsubscribedError, Observable, of } from 'rxjs'; +import { tap, mergeMapTo } from 'rxjs/operators'; /** @test {BehaviorSubject} */ describe('BehaviorSubject', () => { it('should extend Subject', () => { const subject = new BehaviorSubject(null); - expect(subject).to.be.instanceof(Rx.Subject); + expect(subject).to.be.instanceof(Subject); }); it('should throw if it has received an error and getValue() is called', () => { @@ -138,17 +135,19 @@ describe('BehaviorSubject', () => { function feedCompleteIntoSubject() { behaviorSubject.complete(); } const sourceTemplate = '-1-2-3----4------5-6---7--8----9--|'; - const subscriber1 = hot(' (a|) ').mergeMapTo(behaviorSubject); + const subscriber1 = hot(' (a|) ').pipe(mergeMapTo(behaviorSubject)); const unsub1 = ' ! '; const expected1 = ' 3---4------5-6-- '; - const subscriber2 = hot(' (b|) ').mergeMapTo(behaviorSubject); + const subscriber2 = hot(' (b|) ').pipe(mergeMapTo(behaviorSubject)); const unsub2 = ' ! '; const expected2 = ' 4----5-6---7-- '; - const subscriber3 = hot(' (c|) ').mergeMapTo(behaviorSubject); + const subscriber3 = hot(' (c|) ').pipe(mergeMapTo(behaviorSubject)); const expected3 = ' 8---9--|'; - expectObservable(hot(sourceTemplate).do( + expectObservable(hot(sourceTemplate).pipe( + tap( feedNextIntoSubject, feedErrorIntoSubject, feedCompleteIntoSubject + ) )).toBe(sourceTemplate); expectObservable(subscriber1, unsub1).toBe(expected1); expectObservable(subscriber2, unsub2).toBe(expected2); @@ -162,17 +161,21 @@ describe('BehaviorSubject', () => { function feedCompleteIntoSubject() { behaviorSubject.complete(); } const sourceTemplate = '-1-2-3--4--|'; - const subscriber1 = hot(' (a|)').mergeMapTo(behaviorSubject); + const subscriber1 = hot(' (a|)').pipe( + mergeMapTo(behaviorSubject) + ); const expected1 = ' | '; - expectObservable(hot(sourceTemplate).do( - feedNextIntoSubject, feedErrorIntoSubject, feedCompleteIntoSubject + expectObservable(hot(sourceTemplate).pipe( + tap( + feedNextIntoSubject, feedErrorIntoSubject, feedCompleteIntoSubject + ) )).toBe(sourceTemplate); expectObservable(subscriber1).toBe(expected1); }); it('should be an Observer which can be given to Observable.subscribe', (done: MochaDone) => { - const source = Observable.of(1, 2, 3, 4, 5); + const source = of(1, 2, 3, 4, 5); const subject = new BehaviorSubject(0); const expected = [0, 1, 2, 3, 4, 5]; diff --git a/spec/subjects/ReplaySubject-spec.ts b/spec/subjects/ReplaySubject-spec.ts index bc417776ff..e7101af474 100644 --- a/spec/subjects/ReplaySubject-spec.ts +++ b/spec/subjects/ReplaySubject-spec.ts @@ -1,18 +1,16 @@ import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; import { TestScheduler } from '../../src/internal/testing/TestScheduler'; import { hot, expectObservable } from '../helpers/marble-testing'; +import { ReplaySubject, Subject, of } from 'rxjs'; +import { mergeMapTo, tap } from 'rxjs/operators'; declare const rxTestScheduler: TestScheduler; -const ReplaySubject = Rx.ReplaySubject; -const Observable = Rx.Observable; - /** @test {ReplaySubject} */ describe('ReplaySubject', () => { it('should extend Subject', () => { const subject = new ReplaySubject(); - expect(subject).to.be.instanceof(Rx.Subject); + expect(subject).to.be.instanceof(Subject); }); it('should add the observer before running subscription code', () => { @@ -105,18 +103,18 @@ describe('ReplaySubject', () => { function feedCompleteIntoSubject() { replaySubject.complete(); } const sourceTemplate = '-1-2-3----4------5-6---7--8----9--|'; - const subscriber1 = hot(' (a|) ').mergeMapTo(replaySubject); + const subscriber1 = hot(' (a|) ').pipe(mergeMapTo(replaySubject)); const unsub1 = ' ! '; const expected1 = ' (23)4------5-6-- '; - const subscriber2 = hot(' (b|) ').mergeMapTo(replaySubject); + const subscriber2 = hot(' (b|) ').pipe(mergeMapTo(replaySubject)); const unsub2 = ' ! '; const expected2 = ' (34)-5-6---7-- '; - const subscriber3 = hot(' (c|) ').mergeMapTo(replaySubject); + const subscriber3 = hot(' (c|) ').pipe(mergeMapTo(replaySubject)); const expected3 = ' (78)9--|'; - expectObservable(hot(sourceTemplate).do( + expectObservable(hot(sourceTemplate).pipe(tap( feedNextIntoSubject, feedErrorIntoSubject, feedCompleteIntoSubject - )).toBe(sourceTemplate); + ))).toBe(sourceTemplate); expectObservable(subscriber1, unsub1).toBe(expected1); expectObservable(subscriber2, unsub2).toBe(expected2); expectObservable(subscriber3).toBe(expected3); @@ -129,12 +127,12 @@ describe('ReplaySubject', () => { function feedCompleteIntoSubject() { replaySubject.complete(); } const sourceTemplate = '-1-2-3--4--|'; - const subscriber1 = hot(' (a|) ').mergeMapTo(replaySubject); + const subscriber1 = hot(' (a|) ').pipe(mergeMapTo(replaySubject)); const expected1 = ' (34|)'; - expectObservable(hot(sourceTemplate).do( + expectObservable(hot(sourceTemplate).pipe(tap( feedNextIntoSubject, feedErrorIntoSubject, feedCompleteIntoSubject - )).toBe(sourceTemplate); + ))).toBe(sourceTemplate); expectObservable(subscriber1).toBe(expected1); }); @@ -202,18 +200,18 @@ describe('ReplaySubject', () => { function feedCompleteIntoSubject() { replaySubject.complete(); } const sourceTemplate = '-1-2-3----4------5-6----7-8----9--|'; - const subscriber1 = hot(' (a|) ').mergeMapTo(replaySubject); + const subscriber1 = hot(' (a|) ').pipe(mergeMapTo(replaySubject)); const unsub1 = ' ! '; const expected1 = ' (23)4------5-6-- '; - const subscriber2 = hot(' (b|) ').mergeMapTo(replaySubject); + const subscriber2 = hot(' (b|) ').pipe(mergeMapTo(replaySubject)); const unsub2 = ' ! '; const expected2 = ' 4----5-6----7- '; - const subscriber3 = hot(' (c|) ').mergeMapTo(replaySubject); + const subscriber3 = hot(' (c|) ').pipe(mergeMapTo(replaySubject)); const expected3 = ' (78)9--|'; - expectObservable(hot(sourceTemplate).do( + expectObservable(hot(sourceTemplate).pipe(tap( feedNextIntoSubject, feedErrorIntoSubject, feedCompleteIntoSubject - )).toBe(sourceTemplate); + ))).toBe(sourceTemplate); expectObservable(subscriber1, unsub1).toBe(expected1); expectObservable(subscriber2, unsub2).toBe(expected2); expectObservable(subscriber3).toBe(expected3); @@ -226,12 +224,12 @@ describe('ReplaySubject', () => { function feedCompleteIntoSubject() { replaySubject.complete(); } const sourceTemplate = '-1-2-3----4|'; - const subscriber1 = hot(' (a|)').mergeMapTo(replaySubject); + const subscriber1 = hot(' (a|)').pipe(mergeMapTo(replaySubject)); const expected1 = ' (4|)'; - expectObservable(hot(sourceTemplate).do( + expectObservable(hot(sourceTemplate).pipe(tap( feedNextIntoSubject, feedErrorIntoSubject, feedCompleteIntoSubject - )).toBe(sourceTemplate); + ))).toBe(sourceTemplate); expectObservable(subscriber1).toBe(expected1); }); @@ -242,18 +240,18 @@ describe('ReplaySubject', () => { function feedCompleteIntoSubject() { replaySubject.complete(); } const sourceTemplate = '1234 |'; - const subscriber1 = hot(' (a|)').mergeMapTo(replaySubject); + const subscriber1 = hot(' (a|)').pipe(mergeMapTo(replaySubject)); const expected1 = ' (34) |'; - expectObservable(hot(sourceTemplate).do( + expectObservable(hot(sourceTemplate).pipe(tap( feedNextIntoSubject, feedErrorIntoSubject, feedCompleteIntoSubject - )).toBe(sourceTemplate); + ))).toBe(sourceTemplate); expectObservable(subscriber1).toBe(expected1); }); }); it('should be an Observer which can be given to Observable.subscribe', () => { - const source = Observable.of(1, 2, 3, 4, 5); + const source = of(1, 2, 3, 4, 5); const subject = new ReplaySubject(3); let results: (number | string)[] = []; diff --git a/spec/support/default.opts b/spec/support/default.opts index ead4feb92d..32264362e9 100644 --- a/spec/support/default.opts +++ b/spec/support/default.opts @@ -7,7 +7,7 @@ --reporter dot --check-leaks ---globals WebSocket,FormData,XDomainRequest,ActiveXObject +--globals WebSocket,FormData,XDomainRequest,ActiveXObject,fetch,AbortController --recursive --timeout 5000 diff --git a/spec/util/UnsubscriptionError-spec.ts b/spec/util/UnsubscriptionError-spec.ts index de4c2fee85..2799d680f5 100644 --- a/spec/util/UnsubscriptionError-spec.ts +++ b/spec/util/UnsubscriptionError-spec.ts @@ -1,17 +1,15 @@ import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; - -const { Observable, UnsubscriptionError } = Rx; +import { UnsubscriptionError, Observable, timer, merge } from 'rxjs'; /** @test {UnsubscriptionError} */ describe('UnsubscriptionError', () => { it('should create a message that is a clear indication of its internal errors', () => { const err1 = new Error('Swiss cheese tastes amazing but smells like socks'); const err2 = new Error('User too big to fit in tiny European elevator'); - const source1 = Observable.create(() => () => { throw err1; }); - const source2 = Observable.timer(1000); - const source3 = Observable.create(() => () => { throw err2; }); - const source = source1.merge(source2, source3); + const source1 = new Observable(() => () => { throw err1; }); + const source2 = timer(1000); + const source3 = new Observable(() => () => { throw err2; }); + const source = merge(source1, source2, source3); const subscription = source.subscribe(); diff --git a/spec/util/subscribeToResult-spec.ts b/spec/util/subscribeToResult-spec.ts index 7acdb27922..b3b0bfd929 100644 --- a/spec/util/subscribeToResult-spec.ts +++ b/spec/util/subscribeToResult-spec.ts @@ -1,23 +1,25 @@ import { expect } from 'chai'; -import * as Rx from 'rxjs/Rx'; -import { iterator, subscribeToResult, OuterSubscriber } from 'rxjs/internal-compatibility'; +import { OuterSubscriber } from 'rxjs/internal/OuterSubscriber'; +import { subscribeToResult } from 'rxjs/internal/util/subscribeToResult'; +import { iterator } from 'rxjs/internal/symbol/iterator'; import $$symbolObservable from 'symbol-observable'; +import { of, range, throwError } from 'rxjs'; describe('subscribeToResult', () => { - it('should synchronously complete when subscribe to scalarObservable', () => { - const result = Rx.Observable.of(42); + it('should synchronously complete when subscribed to scalarObservable', () => { + const result = of(42); let expected: number; const subscriber = new OuterSubscriber((x) => expected = x); const subscription = subscribeToResult(subscriber, result); expect(expected).to.be.equal(42); - expect(subscription).to.not.exist; + expect(subscription.closed).to.be.true; }); - it('should subscribe to observables that are an instanceof Rx.Observable', (done) => { + it('should subscribe to observables that are an instanceof Observable', (done) => { const expected = [1, 2, 3]; - const result = Rx.Observable.range(1, 3); + const result = range(1, 3); const subscriber = new OuterSubscriber(x => { expect(expected.shift()).to.be.equal(x); @@ -32,7 +34,7 @@ describe('subscribeToResult', () => { }); it('should emit error when observable emits error', (done) => { - const result = Rx.Observable.throwError(new Error('error')); + const result = throwError(new Error('error')); const subscriber = new OuterSubscriber(x => { done(new Error('should not be called')); }, (err) => { @@ -118,7 +120,7 @@ describe('subscribeToResult', () => { }); it('should subscribe to to an object that implements Symbol.observable', (done) => { - const observableSymbolObject = { [$$symbolObservable]: () => Rx.Observable.of(42) }; + const observableSymbolObject = { [$$symbolObservable]: () => of(42) }; const subscriber = new OuterSubscriber(x => { expect(x).to.be.equal(42); diff --git a/src/BUILD.bazel b/src/BUILD.bazel deleted file mode 100644 index 75a58ae0bc..0000000000 --- a/src/BUILD.bazel +++ /dev/null @@ -1,43 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -exports_files(["tsconfig.json"]) - -load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") - -ts_library( - name = "lib", - srcs = glob( - ["internal/**/*.ts"], - # exclude all backwards compatibility code because we don't have a bazel target setup for that - exclude = [ - "internal/Rx.ts", - "internal-compatibility/**", - "internal/patching/**", - "internal/umd.ts", - ], - ), - # Specify the compile-time dependencies to run the compilation (eg. typescript) - # The default value assumes that the end-user has a target //:node_modules - # but not all users do. - # This also makes the build more reproducible, in case the user's TypeScript - # version isn't compatible. - node_modules = "@build_bazel_rules_typescript_tsc_wrapped_deps//:node_modules", - tsconfig = "tsconfig.json", -) - -ts_library( - name = "rxjs", - srcs = ["index.ts"], - module_name = "rxjs", - module_root = "index.d.ts", - # See comment above - node_modules = "@build_bazel_rules_typescript_tsc_wrapped_deps//:node_modules", - tsconfig = "tsconfig.json", - deps = [ - ":lib", - "//ajax", - "//operators", - "//testing", - "//webSocket", - ], -) diff --git a/src/WORKSPACE b/src/WORKSPACE deleted file mode 100644 index 034c99e579..0000000000 --- a/src/WORKSPACE +++ /dev/null @@ -1 +0,0 @@ -workspace(name = "rxjs") diff --git a/src/ajax/BUILD.bazel b/src/ajax/BUILD.bazel deleted file mode 100644 index ec2cf4988a..0000000000 --- a/src/ajax/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") - -ts_library( - name = "ajax", - srcs = ["index.ts"], - module_name = "rxjs/ajax", - module_root = "index.d.ts", - node_modules = "@build_bazel_rules_typescript_tsc_wrapped_deps//:node_modules", - tsconfig = "//:tsconfig.json", - deps = ["//:lib"], -) diff --git a/src/fetch/index.ts b/src/fetch/index.ts new file mode 100644 index 0000000000..e6ff01da27 --- /dev/null +++ b/src/fetch/index.ts @@ -0,0 +1 @@ +export { fromFetch } from '../internal/observable/dom/fetch'; diff --git a/src/fetch/package.json b/src/fetch/package.json new file mode 100644 index 0000000000..dff5519633 --- /dev/null +++ b/src/fetch/package.json @@ -0,0 +1,8 @@ +{ + "name": "rxjs/fetch", + "typings": "./index.d.ts", + "main": "./index.js", + "module": "../_esm5/fetch/index.js", + "es2015": "../_esm2015/fetch/index.js", + "sideEffects": false +} diff --git a/src/index.ts b/src/index.ts index a18251d5f7..624a92be5c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,7 +24,7 @@ export { Subscription } from './internal/Subscription'; export { Subscriber } from './internal/Subscriber'; /* Notification */ -export { Notification } from './internal/Notification'; +export { Notification, NotificationKind } from './internal/Notification'; /* Utils */ export { pipe } from './internal/util/pipe'; @@ -58,12 +58,14 @@ export { never } from './internal/observable/never'; export { of } from './internal/observable/of'; export { onErrorResumeNext } from './internal/observable/onErrorResumeNext'; export { pairs } from './internal/observable/pairs'; +export { partition } from './internal/observable/partition'; export { race } from './internal/observable/race'; export { range } from './internal/observable/range'; export { throwError } from './internal/observable/throwError'; export { timer } from './internal/observable/timer'; export { using } from './internal/observable/using'; export { zip } from './internal/observable/zip'; +export { scheduled } from './internal/scheduled/scheduled'; /* Constants */ export { EMPTY } from './internal/observable/empty'; diff --git a/src/internal/Observable.ts b/src/internal/Observable.ts index f841bdeb87..c677428012 100644 --- a/src/internal/Observable.ts +++ b/src/internal/Observable.ts @@ -6,7 +6,7 @@ import { canReportError } from './util/canReportError'; import { toSubscriber } from './util/toSubscriber'; import { iif } from './observable/iif'; import { throwError } from './observable/throwError'; -import { observable as Symbol_observable } from '../internal/symbol/observable'; +import { observable as Symbol_observable } from './symbol/observable'; import { pipeFromArray } from './util/pipe'; import { config } from './config'; @@ -120,22 +120,26 @@ export class Observable implements Subscribable { * * ## Example * ### Subscribe with an Observer - * ```javascript + * ```ts + * import { of } from 'rxjs'; + * * const sumObserver = { * sum: 0, * next(value) { * console.log('Adding: ' + value); * this.sum = this.sum + value; * }, - * error() { // We actually could just remove this method, - * }, // since we do not really care about errors right now. + * error() { + * // We actually could just remove this method, + * // since we do not really care about errors right now. + * }, * complete() { * console.log('Sum equals: ' + this.sum); * } * }; * - * Rx.Observable.of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes. - * .subscribe(sumObserver); + * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes. + * .subscribe(sumObserver); * * // Logs: * // "Adding: 1" @@ -145,19 +149,18 @@ export class Observable implements Subscribable { * ``` * * ### Subscribe with functions - * ```javascript + * ```ts + * import { of } from 'rxjs' + * * let sum = 0; * - * Rx.Observable.of(1, 2, 3) - * .subscribe( - * function(value) { + * of(1, 2, 3).subscribe( + * value => { * console.log('Adding: ' + value); * sum = sum + value; * }, * undefined, - * function() { - * console.log('Sum equals: ' + sum); - * } + * () => console.log('Sum equals: ' + sum) * ); * * // Logs: @@ -168,13 +171,17 @@ export class Observable implements Subscribable { * ``` * * ### Cancel a subscription - * ```javascript - * const subscription = Rx.Observable.interval(1000).subscribe( + * ```ts + * import { interval } from 'rxjs'; + * + * const subscription = interval(1000).subscribe( * num => console.log(num), * undefined, - * () => console.log('completed!') // Will not be called, even - * ); // when cancelling subscription - * + * () => { + * // Will not be called, even when cancelling subscription. + * console.log('completed!'); + * } + * ); * * setTimeout(() => { * subscription.unsubscribe(); @@ -253,7 +260,7 @@ export class Observable implements Subscribable { promiseCtor = getPromiseCtor(promiseCtor); return new promiseCtor((resolve, reject) => { - // Must be declared in a separate statement to avoid a RefernceError when + // Must be declared in a separate statement to avoid a ReferenceError when // accessing subscription below in the closure due to Temporal Dead Zone. let subscription: Subscription; subscription = this.subscribe((value) => { @@ -318,10 +325,11 @@ export class Observable implements Subscribable { * been called in the order they were passed in. * * ### Example - * ```javascript + * ```ts + * import { interval } from 'rxjs'; * import { map, filter, scan } from 'rxjs/operators'; * - * Rx.Observable.interval(1000) + * interval(1000) * .pipe( * filter(x => x % 2 === 0), * map(x => x + x), diff --git a/src/internal/Subscriber.ts b/src/internal/Subscriber.ts index 68d1622632..4e6558416c 100644 --- a/src/internal/Subscriber.ts +++ b/src/internal/Subscriber.ts @@ -151,14 +151,12 @@ export class Subscriber extends Subscription implements Observer { /** @deprecated This is an internal implementation detail, do not use. */ _unsubscribeAndRecycle(): Subscriber { - const { _parent, _parents } = this; - this._parent = null; - this._parents = null; + const { _parentOrParents } = this; + this._parentOrParents = null; this.unsubscribe(); this.closed = false; this.isStopped = false; - this._parent = _parent; - this._parents = _parents; + this._parentOrParents = _parentOrParents; return this; } } diff --git a/src/internal/Subscription.ts b/src/internal/Subscription.ts index 9ec33b5a17..0f1906a27e 100644 --- a/src/internal/Subscription.ts +++ b/src/internal/Subscription.ts @@ -30,9 +30,7 @@ export class Subscription implements SubscriptionLike { public closed: boolean = false; /** @internal */ - protected _parent: Subscription = null; - /** @internal */ - protected _parents: Subscription[] = null; + protected _parentOrParents: Subscription | Subscription[] = null; /** @internal */ private _subscriptions: SubscriptionLike[] = null; @@ -53,47 +51,40 @@ export class Subscription implements SubscriptionLike { * @return {void} */ unsubscribe(): void { - let hasErrors = false; let errors: any[]; if (this.closed) { return; } - let { _parent, _parents, _unsubscribe, _subscriptions } = ( this); + let { _parentOrParents, _unsubscribe, _subscriptions } = ( this); this.closed = true; - this._parent = null; - this._parents = null; + this._parentOrParents = null; // null out _subscriptions first so any child subscriptions that attempt // to remove themselves from this subscription will noop this._subscriptions = null; - let index = -1; - let len = _parents ? _parents.length : 0; - - // if this._parent is null, then so is this._parents, and we - // don't have to remove ourselves from any parent subscriptions. - while (_parent) { - _parent.remove(this); - // if this._parents is null or index >= len, - // then _parent is set to null, and the loop exits - _parent = ++index < len && _parents[index] || null; + if (_parentOrParents instanceof Subscription) { + _parentOrParents.remove(this); + } else if (_parentOrParents !== null) { + for (let index = 0; index < _parentOrParents.length; ++index) { + const parent = _parentOrParents[index]; + parent.remove(this); + } } if (isFunction(_unsubscribe)) { try { _unsubscribe.call(this); } catch (e) { - hasErrors = true; errors = e instanceof UnsubscriptionError ? flattenUnsubscriptionErrors(e.errors) : [e]; } } if (isArray(_subscriptions)) { - - index = -1; - len = _subscriptions.length; + let index = -1; + let len = _subscriptions.length; while (++index < len) { const sub = _subscriptions[index]; @@ -101,7 +92,6 @@ export class Subscription implements SubscriptionLike { try { sub.unsubscribe(); } catch (e) { - hasErrors = true; errors = errors || []; if (e instanceof UnsubscriptionError) { errors = errors.concat(flattenUnsubscriptionErrors(e.errors)); @@ -113,7 +103,7 @@ export class Subscription implements SubscriptionLike { } } - if (hasErrors) { + if (errors) { throw new UnsubscriptionError(errors); } } @@ -164,14 +154,34 @@ export class Subscription implements SubscriptionLike { } } - if (subscription._addParent(this)) { - // Optimize for the common case when adding the first subscription. - const subscriptions = this._subscriptions; - if (subscriptions) { - subscriptions.push(subscription); - } else { - this._subscriptions = [subscription]; + // Add `this` as parent of `subscription` if that's not already the case. + let { _parentOrParents } = subscription; + if (_parentOrParents === null) { + // If we don't have a parent, then set `subscription._parents` to + // the `this`, which is the common case that we optimize for. + subscription._parentOrParents = this; + } else if (_parentOrParents instanceof Subscription) { + if (_parentOrParents === this) { + // The `subscription` already has `this` as a parent. + return subscription; } + // If there's already one parent, but not multiple, allocate an + // Array to store the rest of the parent Subscriptions. + subscription._parentOrParents = [_parentOrParents, this]; + } else if (_parentOrParents.indexOf(this) === -1) { + // Only add `this` to the _parentOrParents list if it's not already there. + _parentOrParents.push(this); + } else { + // The `subscription` already has `this` as a parent. + return subscription; + } + + // Optimize for the common case when adding the first subscription. + const subscriptions = this._subscriptions; + if (subscriptions === null) { + this._subscriptions = [subscription]; + } else { + subscriptions.push(subscription); } return subscription; @@ -192,29 +202,6 @@ export class Subscription implements SubscriptionLike { } } } - - /** @internal */ - private _addParent(parent: Subscription): boolean { - let { _parent, _parents } = this; - if (_parent === parent) { - // If the new parent is the same as the current parent, then do nothing. - return false; - } else if (!_parent) { - // If we don't have a parent, then set this._parent to the new parent. - this._parent = parent; - return true; - } else if (!_parents) { - // If there's already one parent, but not multiple, allocate an Array to - // store the rest of the parent Subscriptions. - this._parents = [parent]; - return true; - } else if (_parents.indexOf(parent) === -1) { - // Only add the new parent to the _parents list if it's not already there. - _parents.push(parent); - return true; - } - return false; - } } function flattenUnsubscriptionErrors(errors: any[]) { diff --git a/src/internal/observable/ConnectableObservable.ts b/src/internal/observable/ConnectableObservable.ts index c12825f765..2f578293be 100644 --- a/src/internal/observable/ConnectableObservable.ts +++ b/src/internal/observable/ConnectableObservable.ts @@ -45,8 +45,6 @@ export class ConnectableObservable extends Observable { if (connection.closed) { this._connection = null; connection = Subscription.EMPTY; - } else { - this._connection = connection; } } return connection; diff --git a/src/internal/observable/bindCallback.ts b/src/internal/observable/bindCallback.ts index f4e701ac41..b48801d975 100644 --- a/src/internal/observable/bindCallback.ts +++ b/src/internal/observable/bindCallback.ts @@ -111,7 +111,7 @@ export function bindCallback(callbackFunc: Function, scheduler?: SchedulerLike): * ## Examples * * ### Convert jQuery's getJSON to an Observable API - * ```javascript + * ```ts * import { bindCallback } from 'rxjs'; * import * as jQuery from 'jquery'; * @@ -122,14 +122,14 @@ export function bindCallback(callbackFunc: Function, scheduler?: SchedulerLike): * ``` * * ### Receive an array of arguments passed to a callback - * ```javascript + * ```ts * import { bindCallback } from 'rxjs'; * - * someFunction((a, b, c) => { + * const someFunction = (a, b, c) => { * console.log(a); // 5 * console.log(b); // 'some string' * console.log(c); // {someProperty: 'someValue'} - * }); + * }; * * const boundSomeFunction = bindCallback(someFunction); * boundSomeFunction().subscribe(values => { @@ -138,7 +138,7 @@ export function bindCallback(callbackFunc: Function, scheduler?: SchedulerLike): * ``` * * ### Compare behaviour with and without async Scheduler - * ```javascript + * ```ts * import { bindCallback } from 'rxjs'; * * function iCallMyCallbackSynchronously(cb) { @@ -159,7 +159,7 @@ export function bindCallback(callbackFunc: Function, scheduler?: SchedulerLike): * ``` * * ### Use bindCallback on an object method - * ```javascript + * ```ts * import { bindCallback } from 'rxjs'; * * const boundMethod = bindCallback(someObject.methodWithCallback); diff --git a/src/internal/observable/bindNodeCallback.ts b/src/internal/observable/bindNodeCallback.ts index d40e4fb6a4..605961a95b 100644 --- a/src/internal/observable/bindNodeCallback.ts +++ b/src/internal/observable/bindNodeCallback.ts @@ -108,7 +108,7 @@ export function bindNodeCallback(callbackFunc: Function, scheduler?: SchedulerLi * * ## Examples * ### Read a file from the filesystem and get the data as an Observable - * ```javascript + * ```ts * import * as fs from 'fs'; * const readFileAsObservable = bindNodeCallback(fs.readFile); * const result = readFileAsObservable('./roadNames.txt', 'utf8'); @@ -116,7 +116,7 @@ export function bindNodeCallback(callbackFunc: Function, scheduler?: SchedulerLi * ``` * * ### Use on function calling callback with multiple arguments - * ```javascript + * ```ts * someFunction((err, a, b) => { * console.log(err); // null * console.log(a); // 5 @@ -130,7 +130,7 @@ export function bindNodeCallback(callbackFunc: Function, scheduler?: SchedulerLi * ``` * * ### Use on function calling callback in regular style - * ```javascript + * ```ts * someFunction(a => { * console.log(a); // 5 * }); diff --git a/src/internal/observable/combineLatest.ts b/src/internal/observable/combineLatest.ts index cc2f486a12..ce093c9c20 100644 --- a/src/internal/observable/combineLatest.ts +++ b/src/internal/observable/combineLatest.ts @@ -43,27 +43,61 @@ export function combineLatest, O2 extends Observ /** @deprecated resultSelector no longer supported, pipe to map instead */ export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput, O6 extends ObservableInput, R>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, v6: O6, resultSelector: (v1: ObservedValueOf, v2: ObservedValueOf, v3: ObservedValueOf, v4: ObservedValueOf, v5: ObservedValueOf, v6: ObservedValueOf) => R, scheduler?: SchedulerLike): Observable; -// If called with a single array, it "auto-spreads" the array. -export function combineLatest>(sources: [O1], scheduler?: SchedulerLike): Observable<[ObservedValueOf]>; -export function combineLatest, O2 extends ObservableInput>(sources: [O1, O2], scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf]>; -export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput>(sources: [O1, O2, O3], scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf]>; -export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput>(sources: [O1, O2, O3, O4], scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; -export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput>(sources: [O1, O2, O3, O4, O5], scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; -export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput, O6 extends ObservableInput>(sources: [O1, O2, O3, O4, O5, O6], scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; -export function combineLatest>(sources: O[], scheduler?: SchedulerLike): Observable[]>; +// With a scheduler (deprecated) +/** @deprecated Passing a scheduler here is deprecated, use {@link subscribeOn} and/or {@link observeOn} instead */ +export function combineLatest>(sources: [O1], scheduler: SchedulerLike): Observable<[ObservedValueOf]>; +/** @deprecated Passing a scheduler here is deprecated, use {@link subscribeOn} and/or {@link observeOn} instead */ +export function combineLatest, O2 extends ObservableInput>(sources: [O1, O2], scheduler: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf]>; +/** @deprecated Passing a scheduler here is deprecated, use {@link subscribeOn} and/or {@link observeOn} instead */ +export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput>(sources: [O1, O2, O3], scheduler: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +/** @deprecated Passing a scheduler here is deprecated, use {@link subscribeOn} and/or {@link observeOn} instead */ +export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput>(sources: [O1, O2, O3, O4], scheduler: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +/** @deprecated Passing a scheduler here is deprecated, use {@link subscribeOn} and/or {@link observeOn} instead */ +export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput>(sources: [O1, O2, O3, O4, O5], scheduler: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +/** @deprecated Passing a scheduler here is deprecated, use {@link subscribeOn} and/or {@link observeOn} instead */ +export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput, O6 extends ObservableInput>(sources: [O1, O2, O3, O4, O5, O6], scheduler: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +/** @deprecated Passing a scheduler here is deprecated, use {@link subscribeOn} and/or {@link observeOn} instead */ +export function combineLatest>(sources: O[], scheduler: SchedulerLike): Observable[]>; + +// Best case +export function combineLatest>(sources: [O1]): Observable<[ObservedValueOf]>; +export function combineLatest, O2 extends ObservableInput>(sources: [O1, O2]): Observable<[ObservedValueOf, ObservedValueOf]>; +export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput>(sources: [O1, O2, O3]): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput>(sources: [O1, O2, O3, O4]): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput>(sources: [O1, O2, O3, O4, O5]): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput, O6 extends ObservableInput>(sources: [O1, O2, O3, O4, O5, O6]): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +export function combineLatest>(sources: O[]): Observable[]>; // Standard calls +/** @deprecated Pass arguments in a single array instead `combineLatest([a, b, c])` */ export function combineLatest>(v1: O1, scheduler?: SchedulerLike): Observable<[ObservedValueOf]>; +/** @deprecated Pass arguments in a single array instead `combineLatest([a, b, c])` */ export function combineLatest, O2 extends ObservableInput>(v1: O1, v2: O2, scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf]>; +/** @deprecated Pass arguments in a single array instead `combineLatest([a, b, c])` */ export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput>(v1: O1, v2: O2, v3: O3, scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +/** @deprecated Pass arguments in a single array instead `combineLatest([a, b, c])` */ export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +/** @deprecated Pass arguments in a single array instead `combineLatest([a, b, c])` */ export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +/** @deprecated Pass arguments in a single array instead `combineLatest([a, b, c])` */ export function combineLatest, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput, O6 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, v6: O6, scheduler?: SchedulerLike): Observable<[ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf, ObservedValueOf]>; +/** @deprecated Pass arguments in a single array instead `combineLatest([a, b, c])` */ +export function combineLatest>(...observables: O[]): Observable; + +/** @deprecated Pass arguments in a single array instead `combineLatest([a, b, c])` */ +export function combineLatest, R>(...observables: Array | ((...values: Array) => R)>): Observable; + /** @deprecated resultSelector no longer supported, pipe to map instead */ export function combineLatest, R>(array: O[], resultSelector: (...values: ObservedValueOf[]) => R, scheduler?: SchedulerLike): Observable; + +/** @deprecated Passing a scheduler here is deprecated, use {@link subscribeOn} and/or {@link observeOn} instead */ export function combineLatest>(...observables: Array): Observable; + +/** @deprecated Passing a scheduler here is deprecated, use {@link subscribeOn} and/or {@link observeOn} instead */ export function combineLatest, R>(...observables: Array[]) => R) | SchedulerLike>): Observable; + +/** @deprecated Passing a scheduler here is deprecated, use {@link subscribeOn} and/or {@link observeOn} instead */ export function combineLatest(...observables: Array | ((...values: Array) => R) | SchedulerLike>): Observable; /* tslint:enable:max-line-length */ @@ -120,7 +154,7 @@ export function combineLatest(...observables: Array | (( * * ## Examples * ### Combine two timer Observables - * ```javascript + * ```ts * import { combineLatest, timer } from 'rxjs'; * * const firstTimer = timer(0, 1000); // emit 0, 1, 2... after every second, starting from now @@ -135,9 +169,9 @@ export function combineLatest(...observables: Array | (( * ``` * * ### Combine an array of Observables - * ```javascript + * ```ts * import { combineLatest, of } from 'rxjs'; - * import { delat, starWith } from 'rxjs/operators'; + * import { delay, starWith } from 'rxjs/operators'; * * const observables = [1, 5, 10].map( * n => of(n).pipe( @@ -156,7 +190,7 @@ export function combineLatest(...observables: Array | (( * * * ### Use project function to dynamically calculate the Body-Mass Index - * ```javascript + * ```ts * import { combineLatest, of } from 'rxjs'; * import { map } from 'rxjs/operators'; * diff --git a/src/internal/observable/concat.ts b/src/internal/observable/concat.ts index 0137d97b1e..1a93376481 100644 --- a/src/internal/observable/concat.ts +++ b/src/internal/observable/concat.ts @@ -6,13 +6,30 @@ import { from } from './from'; import { concatAll } from '../operators/concatAll'; /* tslint:disable:max-line-length */ -export function concat>(v1: O1, scheduler?: SchedulerLike): Observable>; -export function concat, O2 extends ObservableInput>(v1: O1, v2: O2, scheduler?: SchedulerLike): Observable | ObservedValueOf>; -export function concat, O2 extends ObservableInput, O3 extends ObservableInput>(v1: O1, v2: O2, v3: O3, scheduler?: SchedulerLike): Observable | ObservedValueOf | ObservedValueOf>; -export function concat, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, scheduler?: SchedulerLike): Observable | ObservedValueOf | ObservedValueOf | ObservedValueOf>; -export function concat, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, scheduler?: SchedulerLike): Observable | ObservedValueOf | ObservedValueOf | ObservedValueOf | ObservedValueOf>; -export function concat, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput, O6 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, v6: O6, scheduler?: SchedulerLike): Observable | ObservedValueOf | ObservedValueOf | ObservedValueOf | ObservedValueOf | ObservedValueOf>; +/** @deprecated Use {@link scheduled} and {@link concatAll} (e.g. `scheduled([o1, o2, o3], scheduler).pipe(concatAll())`) */ +export function concat>(v1: O1, scheduler: SchedulerLike): Observable>; +/** @deprecated Use {@link scheduled} and {@link concatAll} (e.g. `scheduled([o1, o2, o3], scheduler).pipe(concatAll())`) */ +export function concat, O2 extends ObservableInput>(v1: O1, v2: O2, scheduler: SchedulerLike): Observable | ObservedValueOf>; +/** @deprecated Use {@link scheduled} and {@link concatAll} (e.g. `scheduled([o1, o2, o3], scheduler).pipe(concatAll())`) */ +export function concat, O2 extends ObservableInput, O3 extends ObservableInput>(v1: O1, v2: O2, v3: O3, scheduler: SchedulerLike): Observable | ObservedValueOf | ObservedValueOf>; +/** @deprecated Use {@link scheduled} and {@link concatAll} (e.g. `scheduled([o1, o2, o3], scheduler).pipe(concatAll())`) */ +export function concat, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, scheduler: SchedulerLike): Observable | ObservedValueOf | ObservedValueOf | ObservedValueOf>; +/** @deprecated Use {@link scheduled} and {@link concatAll} (e.g. `scheduled([o1, o2, o3], scheduler).pipe(concatAll())`) */ +export function concat, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, scheduler: SchedulerLike): Observable | ObservedValueOf | ObservedValueOf | ObservedValueOf | ObservedValueOf>; +/** @deprecated Use {@link scheduled} and {@link concatAll} (e.g. `scheduled([o1, o2, o3], scheduler).pipe(concatAll())`) */ +export function concat, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput, O6 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, v6: O6, scheduler: SchedulerLike): Observable | ObservedValueOf | ObservedValueOf | ObservedValueOf | ObservedValueOf | ObservedValueOf>; + +export function concat>(v1: O1): Observable>; +export function concat, O2 extends ObservableInput>(v1: O1, v2: O2): Observable | ObservedValueOf>; +export function concat, O2 extends ObservableInput, O3 extends ObservableInput>(v1: O1, v2: O2, v3: O3): Observable | ObservedValueOf | ObservedValueOf>; +export function concat, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4): Observable | ObservedValueOf | ObservedValueOf | ObservedValueOf>; +export function concat, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5): Observable | ObservedValueOf | ObservedValueOf | ObservedValueOf | ObservedValueOf>; +export function concat, O2 extends ObservableInput, O3 extends ObservableInput, O4 extends ObservableInput, O5 extends ObservableInput, O6 extends ObservableInput>(v1: O1, v2: O2, v3: O3, v4: O4, v5: O5, v6: O6): Observable | ObservedValueOf | ObservedValueOf | ObservedValueOf | ObservedValueOf | ObservedValueOf>; +export function concat>(...observables: O[]): Observable>; +/** @deprecated Use {@link scheduled} and {@link concatAll} (e.g. `scheduled([o1, o2, o3], scheduler).pipe(concatAll())`) */ export function concat>(...observables: (O | SchedulerLike)[]): Observable>; +export function concat(...observables: ObservableInput[]): Observable; +/** @deprecated Use {@link scheduled} and {@link concatAll} (e.g. `scheduled([o1, o2, o3], scheduler).pipe(concatAll())`) */ export function concat(...observables: (ObservableInput | SchedulerLike)[]): Observable; /* tslint:enable:max-line-length */ /** @@ -54,7 +71,7 @@ export function concat(...observables: (ObservableInput | SchedulerLike) * * ## Examples * ### Concatenate a timer counting from 0 to 3 with a synchronous sequence from 1 to 10 - * ```javascript + * ```ts * import { concat, interval, range } from 'rxjs'; * import { take } from 'rxjs/operators'; * @@ -68,7 +85,7 @@ export function concat(...observables: (ObservableInput | SchedulerLike) * ``` * * ### Concatenate an array of 3 Observables - * ```javascript + * ```ts * import { concat, interval } from 'rxjs'; * import { take } from 'rxjs/operators'; * @@ -86,12 +103,12 @@ export function concat(...observables: (ObservableInput | SchedulerLike) * ``` * * ### Concatenate the same Observable to repeat it - * ```javascript + * ```ts * import { concat, interval } from 'rxjs'; * import { take } from 'rxjs/operators'; * * const timer = interval(1000).pipe(take(2)); - * * + * * concat(timer, timer) // concatenating the same Observable! * .subscribe( * value => console.log(value), diff --git a/src/internal/observable/defer.ts b/src/internal/observable/defer.ts index b4ce810bac..5755abbf6e 100644 --- a/src/internal/observable/defer.ts +++ b/src/internal/observable/defer.ts @@ -23,7 +23,7 @@ import { empty } from './empty'; * * ## Example * ### Subscribe to either an Observable of clicks or an Observable of interval, at random - * ```javascript + * ```ts * import { defer, fromEvent, interval } from 'rxjs'; * * const clicksOrInterval = defer(function () { diff --git a/src/internal/observable/dom/AjaxObservable.ts b/src/internal/observable/dom/AjaxObservable.ts index b3d27f5317..662b8e8701 100644 --- a/src/internal/observable/dom/AjaxObservable.ts +++ b/src/internal/observable/dom/AjaxObservable.ts @@ -109,7 +109,7 @@ export class AjaxObservable extends Observable { * url, headers, etc or a string for a URL. * * ## Example - * ```javascript + * ```ts * import { ajax } from 'rxjs/ajax'; * * const source1 = ajax('/products'); diff --git a/src/internal/observable/dom/ajax.ts b/src/internal/observable/dom/ajax.ts index c524c57adf..059de0ab58 100644 --- a/src/internal/observable/dom/ajax.ts +++ b/src/internal/observable/dom/ajax.ts @@ -5,15 +5,78 @@ import { AjaxObservable, AjaxCreationMethod } from './AjaxObservable'; * It creates an observable for an Ajax request with either a request object with * url, headers, etc or a string for a URL. * + * + * ## Using ajax() to fetch the response object that is being returned from API. + * ```ts + * import { ajax } from 'rxjs/ajax'; + * import { map, catchError } from 'rxjs/operators'; + * import { of } from 'rxjs'; + * + * const obs$ = ajax(`https://api.github.com/users?per_page=5`).pipe( + * map(userResponse => console.log('users: ', userResponse)), + * catchError(error => { + * console.log('error: ', error); + * return of(error); + * }) + * ); + * + * ``` + * * ## Using ajax.getJSON() to fetch data from API. - * ```javascript + * ```ts * import { ajax } from 'rxjs/ajax'; * import { map, catchError } from 'rxjs/operators'; + * import { of } from 'rxjs'; * * const obs$ = ajax.getJSON(`https://api.github.com/users?per_page=5`).pipe( * map(userResponse => console.log('users: ', userResponse)), - * catchError(error => console.log('error: ', error)) + * catchError(error => { + * console.log('error: ', error); + * return of(error); + * }) + * ); + * + * ``` + * + * ## Using ajax() with object as argument and method POST with a two seconds delay. + * ```ts + * import { ajax } from 'rxjs/ajax'; + * import { of } from 'rxjs'; + * + * const users = ajax({ + * url: 'https://httpbin.org/delay/2', + * method: 'POST', + * headers: { + * 'Content-Type': 'application/json', + * 'rxjs-custom-header': 'Rxjs' + * }, + * body: { + * rxjs: 'Hello World!' + * } + * }).pipe( + * map(response => console.log('response: ', response)), + * catchError(error => { + * console.log('error: ', error); + * return of(error); + * }) + * ); + * + * ``` + * + * ## Using ajax() to fetch. An error object that is being returned from the request. + * ```ts + * import { ajax } from 'rxjs/ajax'; + * import { map, catchError } from 'rxjs/operators'; + * import { of } from 'rxjs'; + * + * const obs$ = ajax(`https://api.github.com/404`).pipe( + * map(userResponse => console.log('users: ', userResponse)), + * catchError(error => { + * console.log('error: ', error); + * return of(error); + * }) * ); + * * ``` */ export const ajax: AjaxCreationMethod = AjaxObservable.create; diff --git a/src/internal/observable/dom/fetch.ts b/src/internal/observable/dom/fetch.ts new file mode 100644 index 0000000000..05551c2d21 --- /dev/null +++ b/src/internal/observable/dom/fetch.ts @@ -0,0 +1,90 @@ +import { Observable } from '../../Observable'; + +/** + * Uses [the Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to + * make an HTTP request. + * + * **WARNING** Parts of the fetch API are still experimental. `AbortController` is + * required for this implementation to work and use cancellation appropriately. + * + * Will automatically set up an internal [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) + * in order to teardown the internal `fetch` when the subscription tears down. + * + * If a `signal` is provided via the `init` argument, it will behave like it usually does with + * `fetch`. If the provided `signal` aborts, the error that `fetch` normally rejects with + * in that scenario will be emitted as an error from the observable. + * + * ### Basic Use + * + * ```ts + * import { of } from 'rxjs'; + * import { fetch } from 'rxjs/fetch'; + * import { switchMap, catchError } from 'rxjs/operators'; + * + * const data$ = fetch('https://api.github.com/users?per_page=5').pipe( + * switchMap(response => { + * if(responose.ok) { + * // OK return data + * return response.json(); + * } else { + * // Server is returning a status requiring the client to try something else. + * return of({ error: true, message: `Error ${response.status}` }); + * } + * }), + * catchError(err => { + * // Network or other error, handle appropriately + * console.error(err); + * return of({ error: true, message: error.message }) + * }) + * ); + * + * data$.subscribe({ + * next: result => console.log(result), + * complete: () => console.log('done') + * }) + * ``` + * + * @param input The resource you would like to fetch. Can be a url or a request object. + * @param init A configuration object for the fetch. + * [See MDN for more details](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) + * @returns An Observable, that when subscribed to performs an HTTP request using the native `fetch` + * function. The {@link Subscription} is tied to an `AbortController` for the the fetch. + */ +export function fromFetch(input: string | Request, init?: RequestInit): Observable { + return new Observable(subscriber => { + const controller = new AbortController(); + const signal = controller.signal; + let outerSignalHandler: () => void; + let unsubscribed = false; + + if (init) { + // If we a signal is provided, just have it teardown. It's a cancellation token, basically. + if (init.signal) { + outerSignalHandler = () => { + if (!signal.aborted) { + controller.abort(); + } + }; + init.signal.addEventListener('abort', outerSignalHandler); + } + init.signal = signal; + } else { + init = { signal }; + } + + fetch(input, init).then(response => { + subscriber.next(response); + subscriber.complete(); + }).catch(err => { + if (!unsubscribed) { + // Only forward the error if it wasn't an abort. + subscriber.error(err); + } + }); + + return () => { + unsubscribed = true; + controller.abort(); + }; + }); +} diff --git a/src/internal/observable/dom/webSocket.ts b/src/internal/observable/dom/webSocket.ts index 6413682048..c52b714778 100644 --- a/src/internal/observable/dom/webSocket.ts +++ b/src/internal/observable/dom/webSocket.ts @@ -72,10 +72,11 @@ import { WebSocketSubject, WebSocketSubjectConfig } from './WebSocketSubject'; * might stop sending messages, since it got unsubscription message. This needs to be handled * on the server or using {@link publish} on a Observable returned from 'multiplex'. * - * Last argument to `multiplex` is a `messageFilter` function which filters out messages + * Last argument to `multiplex` is a `messageFilter` function which should return a boolean. It is used to filter out messages * sent by the server to only those that belong to simulated WebSocket stream. For example, server might mark these * messages with some kind of string identifier on a message object and `messageFilter` would return `true` - * if there is such identifier on an object emitted by the socket. + * if there is such identifier on an object emitted by the socket. Messages which returns `false` in `messageFilter` are simply skipped, + * and are not passed down the stream. * * Return value of `multiplex` is an Observable with messages incoming from emulated socket connection. Note that this * is not a `WebSocketSubject`, so calling `next` or `multiplex` again will fail. For pushing values to the @@ -120,7 +121,7 @@ import { WebSocketSubject, WebSocketSubjectConfig } from './WebSocketSubject'; * const observableA = subject.multiplex( * () => JSON.stringify({subscribe: 'A'}), // When server gets this message, it will start sending messages for 'A'... * () => JSON.stringify({unsubscribe: 'A'}), // ...and when gets this one, it will stop. - * message => message.type === 'A' // Server will tag all messages for 'A' with type property. + * message => message.type === 'A' // If the function returns `true` message is passed down the stream. Skipped if the function returns false. * ); * * const observableB = subject.multiplex( // And the same goes for 'B'. @@ -140,7 +141,7 @@ import { WebSocketSubject, WebSocketSubjectConfig } from './WebSocketSubject'; * subB.unsubscribe(); * // Message '{"unsubscribe": "B"}' is sent to the server, which stops sending 'B' messages. * - * subA.unubscribe(); + * subA.unsubscribe(); * // Message '{"unsubscribe": "A"}' makes the server stop sending messages for 'A'. Since there is no more subscribers to root Subject, * // socket connection closes. * ``` diff --git a/src/internal/observable/empty.ts b/src/internal/observable/empty.ts index 649fc49ac4..299d0c1f12 100644 --- a/src/internal/observable/empty.ts +++ b/src/internal/observable/empty.ts @@ -22,7 +22,7 @@ export const EMPTY = new Observable(subscriber => subscriber.complete()); * * ## Examples * ### Emit the number 7, then complete - * ```javascript + * ```ts * import { empty } from 'rxjs'; * import { startWith } from 'rxjs/operators'; * @@ -31,7 +31,7 @@ export const EMPTY = new Observable(subscriber => subscriber.complete()); * ``` * * ### Map and flatten only odd numbers to the sequence 'a', 'b', 'c' - * ```javascript + * ```ts * import { empty, interval } from 'rxjs'; * import { mergeMap } from 'rxjs/operators'; * @@ -53,19 +53,16 @@ export const EMPTY = new Observable(subscriber => subscriber.complete()); * @see {@link of} * @see {@link throwError} * - * @param {SchedulerLike} [scheduler] A {@link SchedulerLike} to use for scheduling + * @param scheduler A {@link SchedulerLike} to use for scheduling * the emission of the complete notification. - * @return {Observable} An "empty" Observable: emits only the complete + * @return An "empty" Observable: emits only the complete * notification. - * @static true - * @name empty - * @owner Observable - * @deprecated Deprecated in favor of using {@link index/EMPTY} constant. + * @deprecated Deprecated in favor of using {@link EMPTY} constant, or {@link scheduled} (e.g. `scheduled([], scheduler)`) */ export function empty(scheduler?: SchedulerLike) { return scheduler ? emptyScheduled(scheduler) : EMPTY; } -export function emptyScheduled(scheduler: SchedulerLike) { +function emptyScheduled(scheduler: SchedulerLike) { return new Observable(subscriber => scheduler.schedule(() => subscriber.complete())); } diff --git a/src/internal/observable/forkJoin.ts b/src/internal/observable/forkJoin.ts index cc4f24a1c2..a7a8d3453a 100644 --- a/src/internal/observable/forkJoin.ts +++ b/src/internal/observable/forkJoin.ts @@ -1,131 +1,130 @@ import { Observable } from '../Observable'; -import { ObservableInput } from '../types'; +import { ObservableInput, ObservedValuesFromArray, ObservedValueOf, SubscribableOrPromise } from '../types'; import { isArray } from '../util/isArray'; -import { EMPTY } from './empty'; -import { subscribeToResult } from '../util/subscribeToResult'; -import { OuterSubscriber } from '../OuterSubscriber'; -import { InnerSubscriber } from '../InnerSubscriber'; -import { Subscriber } from '../Subscriber'; import { map } from '../operators/map'; +import { isObject } from '../util/isObject'; +import { isObservable } from '../util/isObservable'; +import { from } from './from'; /* tslint:disable:max-line-length */ -// forkJoin([a$, b$, c$]); -export function forkJoin(sources: [ObservableInput]): Observable; -export function forkJoin(sources: [ObservableInput, ObservableInput]): Observable<[T, T2]>; -export function forkJoin(sources: [ObservableInput, ObservableInput, ObservableInput]): Observable<[T, T2, T3]>; -export function forkJoin(sources: [ObservableInput, ObservableInput, ObservableInput, ObservableInput]): Observable<[T, T2, T3, T4]>; -export function forkJoin(sources: [ObservableInput, ObservableInput, ObservableInput, ObservableInput, ObservableInput]): Observable<[T, T2, T3, T4, T5]>; -export function forkJoin(sources: [ObservableInput, ObservableInput, ObservableInput, ObservableInput, ObservableInput, ObservableInput]): Observable<[T, T2, T3, T4, T5, T6]>; -export function forkJoin(sources: Array>): Observable; // forkJoin(a$, b$, c$) -export function forkJoin(v1: ObservableInput): Observable; +/** @deprecated Use the version that takes an array of Observables instead */ +export function forkJoin(v1: SubscribableOrPromise): Observable<[T]>; +/** @deprecated Use the version that takes an array of Observables instead */ export function forkJoin(v1: ObservableInput, v2: ObservableInput): Observable<[T, T2]>; +/** @deprecated Use the version that takes an array of Observables instead */ export function forkJoin(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput): Observable<[T, T2, T3]>; +/** @deprecated Use the version that takes an array of Observables instead */ export function forkJoin(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput): Observable<[T, T2, T3, T4]>; +/** @deprecated Use the version that takes an array of Observables instead */ export function forkJoin(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput): Observable<[T, T2, T3, T4, T5]>; +/** @deprecated Use the version that takes an array of Observables instead */ export function forkJoin(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput): Observable<[T, T2, T3, T4, T5, T6]>; +// forkJoin([a$, b$, c$]); +// TODO(benlesh): Uncomment for TS 3.0 +// export function forkJoin(sources: []): Observable; +export function forkJoin(sources: [ObservableInput]): Observable<[A]>; +export function forkJoin(sources: [ObservableInput, ObservableInput]): Observable<[A, B]>; +export function forkJoin(sources: [ObservableInput, ObservableInput, ObservableInput]): Observable<[A, B, C]>; +export function forkJoin(sources: [ObservableInput, ObservableInput, ObservableInput, ObservableInput]): Observable<[A, B, C, D]>; +export function forkJoin(sources: [ObservableInput, ObservableInput, ObservableInput, ObservableInput, ObservableInput]): Observable<[A, B, C, D, E]>; +export function forkJoin(sources: [ObservableInput, ObservableInput, ObservableInput, ObservableInput, ObservableInput, ObservableInput]): Observable<[A, B, C, D, E, F]>; +export function forkJoin[]>(sources: A): Observable[]>; + +// forkJoin({}) +export function forkJoin(sourcesObject: {}): Observable; +export function forkJoin(sourcesObject: T): Observable<{ [K in keyof T]: ObservedValueOf }>; + /** @deprecated resultSelector is deprecated, pipe to map instead */ export function forkJoin(...args: Array|Function>): Observable; +/** @deprecated Use the version that takes an array of Observables instead */ export function forkJoin(...sources: ObservableInput[]): Observable; /* tslint:enable:max-line-length */ /** - * Joins last values emitted by passed Observables. + * Accepts an `Array` of {@link ObservableInput} or a dictionary `Object` of {@link ObservableInput} and returns + * an {@link Observable} that emits either an array of values in the exact same order as the passed array, + * or a dictionary of values in the same shape as the passed dictionary. * * Wait for Observables to complete and then combine last values they emitted. * * ![](forkJoin.png) * - * `forkJoin` is an operator that takes any number of Observables which can be passed either as an array - * or directly as arguments. If no input Observables are provided, resulting stream will complete + * `forkJoin` is an operator that takes any number of input observables which can be passed either as an array + * or a dictionary of input observables. If no input observables are provided, resulting stream will complete * immediately. * - * `forkJoin` will wait for all passed Observables to complete and then it will emit an array with last - * values from corresponding Observables. So if you pass `n` Observables to the operator, resulting - * array will have `n` values, where first value is the last thing emitted by the first Observable, - * second value is the last thing emitted by the second Observable and so on. That means `forkJoin` will - * not emit more than once and it will complete after that. If you need to emit combined values not only - * at the end of lifecycle of passed Observables, but also throughout it, try out {@link combineLatest} + * `forkJoin` will wait for all passed observables to complete and then it will emit an array or an object with last + * values from corresponding observables. + * + * If you pass an array of `n` observables to the operator, resulting + * array will have `n` values, where first value is the last thing emitted by the first observable, + * second value is the last thing emitted by the second observable and so on. + * + * If you pass a dictionary of observables to the operator, resulting + * objects will have the same keys as the dictionary passed, with their last values they've emitted + * located at the corresponding key. + * + * That means `forkJoin` will not emit more than once and it will complete after that. If you need to emit combined + * values not only at the end of lifecycle of passed observables, but also throughout it, try out {@link combineLatest} * or {@link zip} instead. * - * In order for resulting array to have the same length as the number of input Observables, whenever any of - * that Observables completes without emitting any value, `forkJoin` will complete at that moment as well - * and it will not emit anything either, even if it already has some last values from other Observables. - * Conversely, if there is an Observable that never completes, `forkJoin` will never complete as well, - * unless at any point some other Observable completes without emitting value, which brings us back to - * the previous case. Overall, in order for `forkJoin` to emit a value, all Observables passed as arguments + * In order for resulting array to have the same length as the number of input observables, whenever any of + * that observables completes without emitting any value, `forkJoin` will complete at that moment as well + * and it will not emit anything either, even if it already has some last values from other observables. + * Conversely, if there is an observable that never completes, `forkJoin` will never complete as well, + * unless at any point some other observable completes without emitting value, which brings us back to + * the previous case. Overall, in order for `forkJoin` to emit a value, all observables passed as arguments * have to emit something at least once and complete. * - * If any input Observable errors at some point, `forkJoin` will error as well and all other Observables + * If any input observable errors at some point, `forkJoin` will error as well and all other observables * will be immediately unsubscribed. * * Optionally `forkJoin` accepts project function, that will be called with values which normally * would land in emitted array. Whatever is returned by project function, will appear in output - * Observable instead. This means that default project can be thought of as a function that takes + * observable instead. This means that default project can be thought of as a function that takes * all its arguments and puts them into an array. Note that project function will be called only - * when output Observable is supposed to emit a result. + * when output observable is supposed to emit a result. * * ## Examples - * ### Use forkJoin with operator emitting immediately - * ```javascript - * import { forkJoin, of } from 'rxjs'; - * - * const observable = forkJoin( - * of(1, 2, 3, 4), - * of(5, 6, 7, 8), - * ); - * observable.subscribe( - * value => console.log(value), - * err => {}, - * () => console.log('This is how it ends!'), - * ); * - * // Logs: - * // [4, 8] - * // "This is how it ends!" - * ``` + * ### Use forkJoin with a dictionary of observable inputs + * ```ts + * import { forkJoin, of, timer } from 'rxjs'; * - * ### Use forkJoin with operator emitting after some time - * ```javascript - * import { forkJoin, interval } from 'rxjs'; - * import { take } from 'rxjs/operators'; - * - * const observable = forkJoin( - * interval(1000).pipe(take(3)), // emit 0, 1, 2 every second and complete - * interval(500).pipe(take(4)), // emit 0, 1, 2, 3 every half a second and complete - * ); - * observable.subscribe( - * value => console.log(value), - * err => {}, - * () => console.log('This is how it ends!'), - * ); + * const observable = forkJoin({ + * foo: of(1, 2, 3, 4), + * bar: Promise.resolve(8), + * baz: timer(4000), + * }); + * observable.subscribe({ + * next: value => console.log(value), + * complete: () => console.log('This is how it ends!'), + * }); * * // Logs: - * // [2, 3] after 3 seconds + * // { foo: 4, bar: 8, baz: 0 } after 4 seconds * // "This is how it ends!" immediately after * ``` * - * ### Use forkJoin with project function - * ```javascript - * import { forkJoin, interval } from 'rxjs'; - * import { take } from 'rxjs/operators'; - * - * const observable = forkJoin( - * interval(1000).pipe(take(3)), // emit 0, 1, 2 every second and complete - * interval(500).pipe(take(4)), // emit 0, 1, 2, 3 every half a second and complete - * ).pipe( - * map(([n, m]) => n + m), - * ); - * observable.subscribe( - * value => console.log(value), - * err => {}, - * () => console.log('This is how it ends!'), - * ); + * ### Use forkJoin with an array of observable inputs + * ```ts + * import { forkJoin, of } from 'rxjs'; + * + * const observable = forkJoin([ + * of(1, 2, 3, 4), + * Promise.resolve(8), + * timer(4000), + * ]); + * observable.subscribe({ + * next: value => console.log(value), + * complete: () => console.log('This is how it ends!'), + * }); * * // Logs: - * // 5 after 3 seconds + * // [4, 8, 0] after 4 seconds * // "This is how it ends!" immediately after * ``` * @@ -139,93 +138,67 @@ export function forkJoin(...sources: ObservableInput[]): Observable; * @return {Observable} Observable emitting either an array of last values emitted by passed Observables * or value from project function. */ -export function forkJoin( - ...sources: Array | ObservableInput[] | Function> -): Observable { - - let resultSelector: Function; - if (typeof sources[sources.length - 1] === 'function') { - // DEPRECATED PATH - resultSelector = sources.pop() as Function; - } - - // if the first and only other argument is an array - // assume it's been called with `forkJoin([obs1, obs2, obs3])` - if (sources.length === 1 && isArray(sources[0])) { - sources = sources[0] as Array>; - } - - if (sources.length === 0) { - return EMPTY; +export function forkJoin( + ...sources: any[] +): Observable { + if (sources.length === 1) { + const first = sources[0]; + if (isArray(first)) { + return forkJoinInternal(first, null); + } + // TODO(benlesh): isObservable check will not be necessary when deprecated path is removed. + if (isObject(first) && !isObservable(first)) { + const keys = Object.keys(first); + return forkJoinInternal(keys.map(key => first[key]), keys); + } } - if (resultSelector) { - // DEPRECATED PATH - return forkJoin(sources).pipe( - map(args => resultSelector(...args)) + // DEPRECATED PATHS BELOW HERE + if (typeof sources[sources.length - 1] === 'function') { + const resultSelector = sources.pop() as Function; + sources = (sources.length === 1 && isArray(sources[0])) ? sources[0] : sources; + return forkJoinInternal(sources, null).pipe( + map((args: any[]) => resultSelector(...args)) ); } - return new Observable(subscriber => { - return new ForkJoinSubscriber(subscriber, sources as Array>); - }); + return forkJoinInternal(sources, null); } -/** - * We need this JSDoc comment for affecting ESDoc. - * @ignore - * @extends {Ignored} - */ -class ForkJoinSubscriber extends OuterSubscriber { - private completed = 0; - private values: T[]; - private haveValues = 0; - - constructor(destination: Subscriber, - private sources: Array>) { - super(destination); +function forkJoinInternal(sources: ObservableInput[], keys: string[] | null): Observable { + return new Observable(subscriber => { const len = sources.length; - this.values = new Array(len); - - for (let i = 0; i < len; i++) { - const source = sources[i]; - const innerSubscription = subscribeToResult(this, source, null, i); - - if (innerSubscription) { - this.add(innerSubscription); - } - } - } - - notifyNext(outerValue: any, innerValue: T, - outerIndex: number, innerIndex: number, - innerSub: InnerSubscriber): void { - this.values[outerIndex] = innerValue; - if (!(innerSub as any)._hasValue) { - (innerSub as any)._hasValue = true; - this.haveValues++; - } - } - - notifyComplete(innerSub: InnerSubscriber): void { - const { destination, haveValues, values } = this; - const len = values.length; - - if (!(innerSub as any)._hasValue) { - destination.complete(); - return; - } - - this.completed++; - - if (this.completed !== len) { + if (len === 0) { + subscriber.complete(); return; } - - if (haveValues === len) { - destination.next(values); + const values = new Array(len); + let completed = 0; + let emitted = 0; + for (let i = 0; i < len; i++) { + const source = from(sources[i]); + let hasValue = false; + subscriber.add(source.subscribe({ + next: value => { + if (!hasValue) { + hasValue = true; + emitted++; + } + values[i] = value; + }, + error: err => subscriber.error(err), + complete: () => { + completed++; + if (completed === len || !hasValue) { + if (emitted === len) { + subscriber.next(keys ? + keys.reduce((result, key, i) => (result[key] = values[i], result), {}) : + values); + } + subscriber.complete(); + } + } + })); } - - destination.complete(); - } + }); } diff --git a/src/internal/observable/from.ts b/src/internal/observable/from.ts index 2a4f56d64f..697e92860b 100644 --- a/src/internal/observable/from.ts +++ b/src/internal/observable/from.ts @@ -1,16 +1,11 @@ import { Observable } from '../Observable'; -import { isPromise } from '../util/isPromise'; -import { isArrayLike } from '../util/isArrayLike'; -import { isInteropObservable } from '../util/isInteropObservable'; -import { isIterable } from '../util/isIterable'; -import { fromArray } from './fromArray'; -import { fromPromise } from './fromPromise'; -import { fromIterable } from './fromIterable'; -import { fromObservable } from './fromObservable'; import { subscribeTo } from '../util/subscribeTo'; import { ObservableInput, SchedulerLike, ObservedValueOf } from '../types'; +import { scheduled } from '../scheduled/scheduled'; -export function from>(input: O, scheduler?: SchedulerLike): Observable>; +export function from>(input: O): Observable>; +/** @deprecated use {@link scheduled} instead. */ +export function from>(input: O, scheduler: SchedulerLike): Observable>; /** * Creates an Observable from an Array, an array-like object, a Promise, an iterable object, or an Observable-like object. @@ -26,9 +21,11 @@ export function from>(input: O, scheduler?: Sched * converted through this operator. * * ## Examples + * * ### Converts an array to an Observable - * ```javascript - * import { from } from 'rxjs/observable/from'; + * + * ```ts + * import { from } from 'rxjs'; * * const array = [10, 20, 30]; * const result = from(array); @@ -36,15 +33,18 @@ export function from>(input: O, scheduler?: Sched * result.subscribe(x => console.log(x)); * * // Logs: - * // 10 20 30 + * // 10 + * // 20 + * // 30 * ``` * * --- * * ### Convert an infinite iterable (from a generator) to an Observable - * ```javascript + * + * ```ts + * import { from } from 'rxjs'; * import { take } from 'rxjs/operators'; - * import { from } from 'rxjs/observable/from'; * * function* generateDoubles(seed) { * let i = seed; @@ -60,27 +60,40 @@ export function from>(input: O, scheduler?: Sched * result.subscribe(x => console.log(x)); * * // Logs: - * // 3 6 12 24 48 96 192 384 768 1536 + * // 3 + * // 6 + * // 12 + * // 24 + * // 48 + * // 96 + * // 192 + * // 384 + * // 768 + * // 1536 * ``` * * --- * - * ### with async scheduler - * ```javascript - * import { from } from 'rxjs/observable/from'; - * import { async } from 'rxjs/scheduler/async'; + * ### With async scheduler + * + * ```ts + * import { from, asyncScheduler } from 'rxjs'; * * console.log('start'); * * const array = [10, 20, 30]; - * const result = from(array, async); + * const result = from(array, asyncScheduler); * * result.subscribe(x => console.log(x)); * * console.log('end'); * * // Logs: - * // start end 10 20 30 + * // start + * // end + * // 10 + * // 20 + * // 30 * ``` * * @see {@link fromEvent} @@ -93,26 +106,13 @@ export function from>(input: O, scheduler?: Sched * @name from * @owner Observable */ - export function from(input: ObservableInput, scheduler?: SchedulerLike): Observable { if (!scheduler) { if (input instanceof Observable) { return input; } return new Observable(subscribeTo(input)); + } else { + return scheduled(input, scheduler); } - - if (input != null) { - if (isInteropObservable(input)) { - return fromObservable(input, scheduler); - } else if (isPromise(input)) { - return fromPromise(input, scheduler); - } else if (isArrayLike(input)) { - return fromArray(input, scheduler); - } else if (isIterable(input) || typeof input === 'string') { - return fromIterable(input, scheduler); - } - } - - throw new TypeError((input !== null && typeof input || input) + ' is not observable'); } diff --git a/src/internal/observable/fromArray.ts b/src/internal/observable/fromArray.ts index 53ead5bc2a..b5953bebef 100644 --- a/src/internal/observable/fromArray.ts +++ b/src/internal/observable/fromArray.ts @@ -1,26 +1,12 @@ import { Observable } from '../Observable'; import { SchedulerLike } from '../types'; -import { Subscription } from '../Subscription'; import { subscribeToArray } from '../util/subscribeToArray'; +import { scheduleArray } from '../scheduled/scheduleArray'; export function fromArray(input: ArrayLike, scheduler?: SchedulerLike) { if (!scheduler) { return new Observable(subscribeToArray(input)); } else { - return new Observable(subscriber => { - const sub = new Subscription(); - let i = 0; - sub.add(scheduler.schedule(function () { - if (i === input.length) { - subscriber.complete(); - return; - } - subscriber.next(input[i++]); - if (!subscriber.closed) { - sub.add(this.schedule()); - } - })); - return sub; - }); + return scheduleArray(input, scheduler); } } diff --git a/src/internal/observable/fromEvent.ts b/src/internal/observable/fromEvent.ts index d89161efad..e69d127f67 100644 --- a/src/internal/observable/fromEvent.ts +++ b/src/internal/observable/fromEvent.ts @@ -132,7 +132,7 @@ export function fromEvent(target: FromEventTarget, eventName: string, opti * * ## Examples * ### Emits clicks happening on the DOM document - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * * const clicks = fromEvent(document, 'click'); @@ -144,7 +144,7 @@ export function fromEvent(target: FromEventTarget, eventName: string, opti * ``` * * ### Use addEventListener with capture option - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * * const clicksInDocument = fromEvent(document, 'click', true); // note optional configuration parameter diff --git a/src/internal/observable/fromEventPattern.ts b/src/internal/observable/fromEventPattern.ts index 72162c15aa..f83b8d42b6 100644 --- a/src/internal/observable/fromEventPattern.ts +++ b/src/internal/observable/fromEventPattern.ts @@ -58,7 +58,7 @@ export function fromEventPattern(addHandler: (handler: NodeEventHandler) => a * ## Example * ### Emits clicks happening on the DOM document * - * ```javascript + * ```ts * import { fromEventPattern } from 'rxjs'; * * function addClickHandler(handler) { @@ -82,7 +82,7 @@ export function fromEventPattern(addHandler: (handler: NodeEventHandler) => a * ## Example * ### Use with API that returns cancellation token * - * ```javascript + * ```ts * import { fromEventPattern } from 'rxjs'; * * const token = someAPI.registerEventHandler(function() {}); @@ -98,7 +98,7 @@ export function fromEventPattern(addHandler: (handler: NodeEventHandler) => a * ## Example * ### Use with project function * - * ```javascript + * ```ts * import { fromEventPattern } from 'rxjs'; * * someAPI.registerEventHandler((eventType, eventMessage) => { diff --git a/src/internal/observable/fromIterable.ts b/src/internal/observable/fromIterable.ts index 94e3719ba0..e7ffd2b2aa 100644 --- a/src/internal/observable/fromIterable.ts +++ b/src/internal/observable/fromIterable.ts @@ -1,50 +1,15 @@ import { Observable } from '../Observable'; import { SchedulerLike } from '../types'; -import { Subscription } from '../Subscription'; -import { iterator as Symbol_iterator } from '../symbol/iterator'; import { subscribeToIterable } from '../util/subscribeToIterable'; +import { scheduleIterable } from '../scheduled/scheduleIterable'; -export function fromIterable(input: Iterable, scheduler: SchedulerLike) { +export function fromIterable(input: Iterable, scheduler?: SchedulerLike) { if (!input) { throw new Error('Iterable cannot be null'); } if (!scheduler) { return new Observable(subscribeToIterable(input)); } else { - return new Observable(subscriber => { - const sub = new Subscription(); - let iterator: Iterator; - sub.add(() => { - // Finalize generators - if (iterator && typeof iterator.return === 'function') { - iterator.return(); - } - }); - sub.add(scheduler.schedule(() => { - iterator = input[Symbol_iterator](); - sub.add(scheduler.schedule(function () { - if (subscriber.closed) { - return; - } - let value: T; - let done: boolean; - try { - const result = iterator.next(); - value = result.value; - done = result.done; - } catch (err) { - subscriber.error(err); - return; - } - if (done) { - subscriber.complete(); - } else { - subscriber.next(value); - this.schedule(); - } - })); - })); - return sub; - }); + return scheduleIterable(input, scheduler); } } diff --git a/src/internal/observable/fromObservable.ts b/src/internal/observable/fromObservable.ts index c3f3e06567..6a297b4469 100644 --- a/src/internal/observable/fromObservable.ts +++ b/src/internal/observable/fromObservable.ts @@ -1,24 +1,12 @@ import { Observable } from '../Observable'; -import { Subscription } from '../Subscription'; -import { observable as Symbol_observable } from '../symbol/observable'; import { subscribeToObservable } from '../util/subscribeToObservable'; -import { InteropObservable, SchedulerLike, Subscribable } from '../types'; +import { InteropObservable, SchedulerLike } from '../types'; +import { scheduleObservable } from '../scheduled/scheduleObservable'; -export function fromObservable(input: InteropObservable, scheduler: SchedulerLike) { +export function fromObservable(input: InteropObservable, scheduler?: SchedulerLike) { if (!scheduler) { return new Observable(subscribeToObservable(input)); } else { - return new Observable(subscriber => { - const sub = new Subscription(); - sub.add(scheduler.schedule(() => { - const observable: Subscribable = input[Symbol_observable](); - sub.add(observable.subscribe({ - next(value) { sub.add(scheduler.schedule(() => subscriber.next(value))); }, - error(err) { sub.add(scheduler.schedule(() => subscriber.error(err))); }, - complete() { sub.add(scheduler.schedule(() => subscriber.complete())); }, - })); - })); - return sub; - }); + return scheduleObservable(input, scheduler); } } diff --git a/src/internal/observable/fromPromise.ts b/src/internal/observable/fromPromise.ts index 214e2cede4..28ebef65ea 100644 --- a/src/internal/observable/fromPromise.ts +++ b/src/internal/observable/fromPromise.ts @@ -1,26 +1,12 @@ import { Observable } from '../Observable'; import { SchedulerLike } from '../types'; -import { Subscription } from '../Subscription'; import { subscribeToPromise } from '../util/subscribeToPromise'; +import { schedulePromise } from '../scheduled/schedulePromise'; export function fromPromise(input: PromiseLike, scheduler?: SchedulerLike) { if (!scheduler) { return new Observable(subscribeToPromise(input)); } else { - return new Observable(subscriber => { - const sub = new Subscription(); - sub.add(scheduler.schedule(() => input.then( - value => { - sub.add(scheduler.schedule(() => { - subscriber.next(value); - sub.add(scheduler.schedule(() => subscriber.complete())); - })); - }, - err => { - sub.add(scheduler.schedule(() => subscriber.error(err))); - } - ))); - return sub; - }); + return schedulePromise(input, scheduler); } } diff --git a/src/internal/observable/iif.ts b/src/internal/observable/iif.ts index 3b3248427e..25dfbd0cd7 100644 --- a/src/internal/observable/iif.ts +++ b/src/internal/observable/iif.ts @@ -27,7 +27,7 @@ import { SubscribableOrPromise } from '../types'; * * ## Examples * ### Change at runtime which Observable will be subscribed - * ```javascript + * ```ts * import { iif, of } from 'rxjs'; * * let subscribeToFirst; @@ -52,7 +52,7 @@ import { SubscribableOrPromise } from '../types'; * ``` * * ### Control an access to an Observable - * ```javascript + * ```ts * let accessGranted; * const observableIfYouHaveAccess = iif( * () => accessGranted, diff --git a/src/internal/observable/interval.ts b/src/internal/observable/interval.ts index b4d1ef0ebb..fc9d3f9bc9 100644 --- a/src/internal/observable/interval.ts +++ b/src/internal/observable/interval.ts @@ -22,7 +22,7 @@ import { Subscriber } from '../Subscriber'; * * ## Example * Emits ascending numbers, one every second (1000ms) up to the number 3 - * ```javascript + * ```ts * import { interval } from 'rxjs'; * import { take } from 'rxjs/operators'; * diff --git a/src/internal/observable/merge.ts b/src/internal/observable/merge.ts index 569e6f0ced..f9f1faa77a 100644 --- a/src/internal/observable/merge.ts +++ b/src/internal/observable/merge.ts @@ -5,19 +5,48 @@ import { mergeAll } from '../operators/mergeAll'; import { fromArray } from './fromArray'; /* tslint:disable:max-line-length */ -export function merge(v1: ObservableInput, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, concurrent?: number, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, v2: ObservableInput, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, v2: ObservableInput, concurrent?: number, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, concurrent?: number, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, concurrent?: number, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, concurrent?: number, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput, scheduler?: SchedulerLike): Observable; -export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput, concurrent?: number, scheduler?: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, concurrent: number, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, v2: ObservableInput, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, v2: ObservableInput, concurrent: number, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, concurrent: number, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, concurrent: number, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, concurrent: number, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput, concurrent: number, scheduler: SchedulerLike): Observable; + +export function merge(v1: ObservableInput): Observable; +export function merge(v1: ObservableInput, concurrent?: number): Observable; +export function merge(v1: ObservableInput, v2: ObservableInput): Observable; +export function merge(v1: ObservableInput, v2: ObservableInput, concurrent?: number): Observable; +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput): Observable; +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, concurrent?: number): Observable; +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput): Observable; +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, concurrent?: number): Observable; +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput): Observable; +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, concurrent?: number): Observable; +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput): Observable; +export function merge(v1: ObservableInput, v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput, concurrent?: number): Observable; +export function merge(...observables: (ObservableInput | number)[]): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ export function merge(...observables: (ObservableInput | SchedulerLike | number)[]): Observable; +export function merge(...observables: (ObservableInput | number)[]): Observable; +/** @deprecated use {@link scheduled} and {@link mergeAll} (e.g. `scheduled([ob1, ob2, ob3], scheduled).pipe(mergeAll())*/ export function merge(...observables: (ObservableInput | SchedulerLike | number)[]): Observable; /* tslint:enable:max-line-length */ /** @@ -37,7 +66,7 @@ export function merge(...observables: (ObservableInput | SchedulerLik * * ## Examples * ### Merge together two Observables: 1s interval and clicks - * ```javascript + * ```ts * import { merge, fromEvent, interval } from 'rxjs'; * * const clicks = fromEvent(document, 'click'); @@ -53,7 +82,7 @@ export function merge(...observables: (ObservableInput | SchedulerLik * ``` * * ### Merge together 3 Observables, but only 2 run concurrently - * ```javascript + * ```ts * import { merge, interval } from 'rxjs'; * import { take } from 'rxjs/operators'; * diff --git a/src/internal/observable/never.ts b/src/internal/observable/never.ts index c08b5f5961..280ea4ed02 100644 --- a/src/internal/observable/never.ts +++ b/src/internal/observable/never.ts @@ -14,7 +14,7 @@ import { noop } from '../util/noop'; * * ## Example * ### Emit the number 7, then never emit anything else (not even complete) - * ```javascript + * ```ts * import { NEVER } from 'rxjs'; * import { startWith } from 'rxjs/operators'; * diff --git a/src/internal/observable/of.ts b/src/internal/observable/of.ts index 281ff6fcae..0d1411b5d0 100644 --- a/src/internal/observable/of.ts +++ b/src/internal/observable/of.ts @@ -1,24 +1,48 @@ import { SchedulerLike } from '../types'; import { isScheduler } from '../util/isScheduler'; import { fromArray } from './fromArray'; -import { empty } from './empty'; -import { scalar } from './scalar'; import { Observable } from '../Observable'; +import { scheduleArray } from '../scheduled/scheduleArray'; /* tslint:disable:max-line-length */ -export function of(a: T, scheduler?: SchedulerLike): Observable; -export function of(a: T, b: T2, scheduler?: SchedulerLike): Observable; -export function of(a: T, b: T2, c: T3, scheduler?: SchedulerLike): Observable; -export function of(a: T, b: T2, c: T3, d: T4, scheduler?: SchedulerLike): Observable; -export function of(a: T, b: T2, c: T3, d: T4, e: T5, scheduler?: SchedulerLike): Observable; -export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, scheduler?: SchedulerLike): Observable; -export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, scheduler?: SchedulerLike): +/** @deprecated use {@link scheduled} instead `scheduled([a, b, c], scheduler)` */ +export function of(a: T, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} instead `scheduled([a, b, c], scheduler)` */ +export function of(a: T, b: T2, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} instead `scheduled([a, b, c], scheduler)` */ +export function of(a: T, b: T2, c: T3, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} instead `scheduled([a, b, c], scheduler)` */ +export function of(a: T, b: T2, c: T3, d: T4, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} instead `scheduled([a, b, c], scheduler)` */ +export function of(a: T, b: T2, c: T3, d: T4, e: T5, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} instead `scheduled([a, b, c], scheduler)` */ +export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, scheduler: SchedulerLike): Observable; +/** @deprecated use {@link scheduled} instead `scheduled([a, b, c], scheduler)` */ +export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, scheduler: SchedulerLike): Observable; -export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, h: T8, scheduler?: SchedulerLike): +/** @deprecated use {@link scheduled} instead `scheduled([a, b, c], scheduler)` */ +export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, h: T8, scheduler: SchedulerLike): Observable; -export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, h: T8, i: T9, scheduler?: SchedulerLike): +/** @deprecated use {@link scheduled} instead `scheduled([a, b, c], scheduler)` */ +export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, h: T8, i: T9, scheduler: SchedulerLike): Observable; -export function of(...args: Array): Observable; +/** @deprecated use {@link scheduled} instead `scheduled([a, b, c], scheduler)` */ +export function of(...args: (T | SchedulerLike)[]): Observable; + +// TODO(benlesh): Update the typings for this when we can switch to TS 3.x +export function of(a: T): Observable; +export function of(a: T, b: T2): Observable; +export function of(a: T, b: T2, c: T3): Observable; +export function of(a: T, b: T2, c: T3, d: T4): Observable; +export function of(a: T, b: T2, c: T3, d: T4, e: T5): Observable; +export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6): Observable; +export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7): + Observable; +export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, h: T8): + Observable; +export function of(a: T, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, h: T8, i: T9): + Observable; +export function of(...args: T[]): Observable; /* tslint:enable:max-line-length */ /** @@ -35,7 +59,7 @@ export function of(...args: Array): Observable; * * Emit the values `10, 20, 30` * - * ```javascript + * ```ts * import { of } from 'rxjs'; * * of(10, 20, 30) @@ -53,7 +77,7 @@ export function of(...args: Array): Observable; * * Emit the array `[1,2,3]` * - * ```javascript + * ```ts * import { of } from 'rxjs'; * * of([1,2,3]) @@ -80,15 +104,8 @@ export function of(...args: Array): Observable { let scheduler = args[args.length - 1] as SchedulerLike; if (isScheduler(scheduler)) { args.pop(); + return scheduleArray(args as T[], scheduler); } else { - scheduler = undefined; - } - switch (args.length) { - case 0: - return empty(scheduler); - case 1: - return scheduler ? fromArray(args as T[], scheduler) : scalar(args[0] as T); - default: - return fromArray(args as T[], scheduler); + return fromArray(args as T[]); } } diff --git a/src/internal/observable/onErrorResumeNext.ts b/src/internal/observable/onErrorResumeNext.ts index 179d9b41fa..79d8510934 100644 --- a/src/internal/observable/onErrorResumeNext.ts +++ b/src/internal/observable/onErrorResumeNext.ts @@ -38,7 +38,7 @@ export function onErrorResumeNext(array: ObservableInput[]): Observable< * * ## Example * Subscribe to the next Observable after map fails - * ```javascript + * ```ts * import { onErrorResumeNext, of } from 'rxjs'; * import { map } from 'rxjs/operators'; * diff --git a/src/internal/observable/pairs.ts b/src/internal/observable/pairs.ts index e990ca770d..85bd81c5b3 100644 --- a/src/internal/observable/pairs.ts +++ b/src/internal/observable/pairs.ts @@ -21,7 +21,7 @@ import { Subscription } from '../Subscription'; * pass a {@link SchedulerLike} as a second argument to `pairs`. * * @example Converts a javascript object to an Observable - * ```javascript + * ```ts * import { pairs } from 'rxjs'; * * const obj = { diff --git a/src/internal/observable/partition.ts b/src/internal/observable/partition.ts new file mode 100644 index 0000000000..637172fc86 --- /dev/null +++ b/src/internal/observable/partition.ts @@ -0,0 +1,67 @@ +import { not } from '../util/not'; +import { subscribeTo } from '../util/subscribeTo'; +import { filter } from '../operators/filter'; +import { ObservableInput } from '../types'; +import { Observable } from '../Observable'; + +/** + * Splits the source Observable into two, one with values that satisfy a + * predicate, and another with values that don't satisfy the predicate. + * + * It's like {@link filter}, but returns two Observables: + * one like the output of {@link filter}, and the other with values that did not + * pass the condition. + * + * ![](partition.png) + * + * `partition` outputs an array with two Observables that partition the values + * from the source Observable through the given `predicate` function. The first + * Observable in that array emits source values for which the predicate argument + * returns true. The second Observable emits source values for which the + * predicate returns false. The first behaves like {@link filter} and the second + * behaves like {@link filter} with the predicate negated. + * + * ## Example + * Partition a set of numbers into odds and evens observables + * ```ts + * import { of, partition } from 'rxjs'; + * + * const observableValues = of(1, 2, 3, 4, 5, 6); + * const [evens$, odds$] = partition(observableValues, (value, index) => value % 2 === 0); + * + * odds$.subscribe(x => console.log('odds', x)); + * evens$.subscribe(x => console.log('evens', x)); + * + * // Logs: + * // odds 1 + * // odds 3 + * // odds 5 + * // evens 2 + * // evens 4 + * // evens 6 + * ``` + * + * @see {@link filter} + * + * @param {function(value: T, index: number): boolean} predicate A function that + * evaluates each value emitted by the source Observable. If it returns `true`, + * the value is emitted on the first Observable in the returned array, if + * `false` the value is emitted on the second Observable in the array. The + * `index` parameter is the number `i` for the i-th source emission that has + * happened since the subscription, starting from the number `0`. + * @param {any} [thisArg] An optional argument to determine the value of `this` + * in the `predicate` function. + * @return {[Observable, Observable]} An array with two Observables: one + * with values that passed the predicate, and another with values that did not + * pass the predicate. + */ +export function partition( + source: ObservableInput, + predicate: (value: T, index: number) => boolean, + thisArg?: any +): [Observable, Observable] { + return [ + filter(predicate, thisArg)(new Observable(subscribeTo(source))), + filter(not(predicate, thisArg) as any)(new Observable(subscribeTo(source))) + ] as [Observable, Observable]; +} diff --git a/src/internal/observable/race.ts b/src/internal/observable/race.ts index 7a1015c02f..e6e13ded30 100644 --- a/src/internal/observable/race.ts +++ b/src/internal/observable/race.ts @@ -4,22 +4,31 @@ import { fromArray } from './fromArray'; import { Operator } from '../Operator'; import { Subscriber } from '../Subscriber'; import { Subscription } from '../Subscription'; -import { TeardownLogic } from '../types'; +import { TeardownLogic, ObservableInput } from '../types'; import { OuterSubscriber } from '../OuterSubscriber'; import { InnerSubscriber } from '../InnerSubscriber'; import { subscribeToResult } from '../util/subscribeToResult'; // tslint:disable:max-line-length -export function race(a: Observable, b: Observable): Observable | Observable; -export function race(a: Observable, b: Observable, c: Observable): Observable | Observable | Observable; -export function race(a: Observable, b: Observable, c: Observable, d: Observable): Observable | Observable | Observable | Observable; -export function race(a: Observable, b: Observable, c: Observable, d: Observable, e: Observable): Observable | Observable | Observable | Observable | Observable; +export function race(arg: [ObservableInput]): Observable; +export function race(arg: [ObservableInput, ObservableInput]): Observable; +export function race(arg: [ObservableInput, ObservableInput, ObservableInput]): Observable; +export function race(arg: [ObservableInput, ObservableInput, ObservableInput, ObservableInput]): Observable; +export function race(arg: [ObservableInput, ObservableInput, ObservableInput, ObservableInput, ObservableInput]): Observable; +export function race(arg: ObservableInput[]): Observable; +export function race(arg: ObservableInput[]): Observable<{}>; + +export function race(a: ObservableInput): Observable; +export function race(a: ObservableInput, b: ObservableInput): Observable; +export function race(a: ObservableInput, b: ObservableInput, c: ObservableInput): Observable; +export function race(a: ObservableInput, b: ObservableInput, c: ObservableInput, d: ObservableInput): Observable; +export function race(a: ObservableInput, b: ObservableInput, c: ObservableInput, d: ObservableInput, e: ObservableInput): Observable; // tslint:enable:max-line-length -export function race(observables: Observable[]): Observable; -export function race(observables: Observable[]): Observable<{}>; -export function race(...observables: Observable[]): Observable; -export function race(...observables: Observable[]): Observable<{}>; +export function race(observables: ObservableInput[]): Observable; +export function race(observables: ObservableInput[]): Observable<{}>; +export function race(...observables: ObservableInput[]): Observable; +export function race(...observables: ObservableInput[]): Observable<{}>; /** * Returns an Observable that mirrors the first source Observable to emit an item. @@ -27,7 +36,7 @@ export function race(...observables: Observable[]): Observable<{}>; * ## Example * ### Subscribes to the observable that was the first to start emitting. * - * ```javascript + * ```ts * import { race, interval } from 'rxjs'; * import { mapTo } from 'rxjs/operators'; * @@ -50,7 +59,7 @@ export function race(...observables: Observable[]): Observable<{}>; * @name race * @owner Observable */ -export function race(...observables: (Observable[] | Observable)[]): Observable { +export function race(...observables: ObservableInput[]): Observable { // if the only argument is an array, it was most likely called with // `race([obs1, obs2, ...])` if (observables.length === 1) { diff --git a/src/internal/observable/range.ts b/src/internal/observable/range.ts index 671f7049fb..316d378f09 100644 --- a/src/internal/observable/range.ts +++ b/src/internal/observable/range.ts @@ -16,7 +16,7 @@ import { Observable } from '../Observable'; * * ## Example * Emits the numbers 1 to 10 - * ```javascript + * ```ts * import { range } from 'rxjs'; * * const numbers = range(1, 10); diff --git a/src/internal/observable/scalar.ts b/src/internal/observable/scalar.ts deleted file mode 100644 index 6d66b7ea07..0000000000 --- a/src/internal/observable/scalar.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Observable } from '../Observable'; - -export function scalar(value: T) { - const result = new Observable(subscriber => { - subscriber.next(value); - subscriber.complete(); - }); - result._isScalar = true; - (result as any).value = value; - return result; -} diff --git a/src/internal/observable/throwError.ts b/src/internal/observable/throwError.ts index 96ccb4aade..4beb397463 100644 --- a/src/internal/observable/throwError.ts +++ b/src/internal/observable/throwError.ts @@ -17,7 +17,7 @@ import { Subscriber } from '../Subscriber'; * * ## Examples * ### Emit the number 7, then emit an error - * ```javascript + * ```ts * import { throwError, concat, of } from 'rxjs'; * * const result = concat(of(7), throwError(new Error('oops!'))); @@ -31,7 +31,7 @@ import { Subscriber } from '../Subscriber'; * --- * * ### Map and flatten numbers to the sequence 'a', 'b', 'c', but throw an error for 2 - * ```javascript + * ```ts * import { throwError, interval, of } from 'rxjs'; * import { mergeMap } from 'rxjs/operators'; * diff --git a/src/internal/observable/timer.ts b/src/internal/observable/timer.ts index 9131dee3bd..a0610607a5 100644 --- a/src/internal/observable/timer.ts +++ b/src/internal/observable/timer.ts @@ -25,7 +25,7 @@ import { Subscriber } from '../Subscriber'; * * ## Examples * ### Emits ascending numbers, one every second (1000ms), starting after 3 seconds - * ```javascript + * ```ts * import { timer } from 'rxjs'; * * const numbers = timer(3000, 1000); @@ -33,7 +33,7 @@ import { Subscriber } from '../Subscriber'; * ``` * * ### Emits one number after five seconds - * ```javascript + * ```ts * import { timer } from 'rxjs'; * * const numbers = timer(5000); diff --git a/src/internal/observable/zip.ts b/src/internal/observable/zip.ts index 18a0cce688..fbe0756573 100644 --- a/src/internal/observable/zip.ts +++ b/src/internal/observable/zip.ts @@ -51,7 +51,7 @@ export function zip(...observables: Array | ((...values: * * ## Example * Combine age and name from different sources - * ```javascript + * ```ts * import { zip, of } from 'rxjs'; * import { map } from 'rxjs/operators'; * diff --git a/src/internal/operators/audit.ts b/src/internal/operators/audit.ts index 04992c0fd9..9028abdb6d 100644 --- a/src/internal/operators/audit.ts +++ b/src/internal/operators/audit.ts @@ -31,7 +31,7 @@ import { subscribeToResult } from '../util/subscribeToResult'; * ## Example * * Emit clicks at a rate of at most one click per second - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { audit } from 'rxjs/operators' * diff --git a/src/internal/operators/auditTime.ts b/src/internal/operators/auditTime.ts index 9388d7755b..50facdc9e1 100644 --- a/src/internal/operators/auditTime.ts +++ b/src/internal/operators/auditTime.ts @@ -7,7 +7,7 @@ import { MonoTypeOperatorFunction, SchedulerLike } from '../types'; * Ignores source values for `duration` milliseconds, then emits the most recent * value from the source Observable, then repeats this process. * - * When it sees a source values, it ignores that plus + * When it sees a source value, it ignores that plus * the next ones for `duration` milliseconds, and then it emits the most recent * value from the source. * @@ -27,7 +27,7 @@ import { MonoTypeOperatorFunction, SchedulerLike } from '../types'; * ## Example * * Emit clicks at a rate of at most one click per second - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { auditTime } from 'rxjs/operators'; * diff --git a/src/internal/operators/buffer.ts b/src/internal/operators/buffer.ts index 74fb11de45..31b4bb3987 100644 --- a/src/internal/operators/buffer.ts +++ b/src/internal/operators/buffer.ts @@ -23,13 +23,13 @@ import { OperatorFunction } from '../types'; * * On every click, emit array of most recent interval events * - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { buffer } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); - * const interval = interval(1000); - * const buffered = interval.pipe(buffer(clicks)); + * const intervalEvents = interval(1000); + * const buffered = intervalEvents.pipe(buffer(clicks)); * buffered.subscribe(x => console.log(x)); * ``` * diff --git a/src/internal/operators/bufferCount.ts b/src/internal/operators/bufferCount.ts index 0ce86a661d..ea01b6a663 100644 --- a/src/internal/operators/bufferCount.ts +++ b/src/internal/operators/bufferCount.ts @@ -22,7 +22,7 @@ import { OperatorFunction, TeardownLogic } from '../types'; * * Emit the last two click events as an array * - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { bufferCount } from 'rxjs/operators'; * @@ -33,7 +33,7 @@ import { OperatorFunction, TeardownLogic } from '../types'; * * On every click, emit the last two click events as an array * - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { bufferCount } from 'rxjs/operators'; * diff --git a/src/internal/operators/bufferTime.ts b/src/internal/operators/bufferTime.ts index 50099d3baa..6edc64bd8d 100644 --- a/src/internal/operators/bufferTime.ts +++ b/src/internal/operators/bufferTime.ts @@ -33,7 +33,7 @@ export function bufferTime(bufferTimeSpan: number, bufferCreationInterval: nu * * Every second, emit an array of the recent click events * - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { bufferTime } from 'rxjs/operators'; * @@ -44,7 +44,7 @@ export function bufferTime(bufferTimeSpan: number, bufferCreationInterval: nu * * Every 5 seconds, emit the click events from the next 2 seconds * - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { bufferTime } from 'rxjs/operators'; * diff --git a/src/internal/operators/bufferToggle.ts b/src/internal/operators/bufferToggle.ts index 6385730ba5..8676fa7495 100644 --- a/src/internal/operators/bufferToggle.ts +++ b/src/internal/operators/bufferToggle.ts @@ -25,14 +25,14 @@ import { OperatorFunction, SubscribableOrPromise } from '../types'; * * Every other second, emit the click events from the next 500ms * - * ```javascript - * import { fromEvent, interval, empty } from 'rxjs'; + * ```ts + * import { fromEvent, interval, EMPTY } from 'rxjs'; * import { bufferToggle } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); * const openings = interval(1000); * const buffered = clicks.pipe(bufferToggle(openings, i => - * i % 2 ? interval(500) : empty() + * i % 2 ? interval(500) : EMPTY * )); * buffered.subscribe(x => console.log(x)); * ``` diff --git a/src/internal/operators/bufferWhen.ts b/src/internal/operators/bufferWhen.ts index 2b8467d37e..2c3e96bc2e 100644 --- a/src/internal/operators/bufferWhen.ts +++ b/src/internal/operators/bufferWhen.ts @@ -25,7 +25,7 @@ import { OperatorFunction } from '../types'; * * Emit an array of the last clicks every [1-5] random seconds * - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { bufferWhen } from 'rxjs/operators'; * diff --git a/src/internal/operators/catchError.ts b/src/internal/operators/catchError.ts index a772def44f..73037a4cb5 100644 --- a/src/internal/operators/catchError.ts +++ b/src/internal/operators/catchError.ts @@ -1,11 +1,11 @@ -import {Operator} from '../Operator'; -import {Subscriber} from '../Subscriber'; -import {Observable} from '../Observable'; +import { Operator } from '../Operator'; +import { Subscriber } from '../Subscriber'; +import { Observable } from '../Observable'; -import {OuterSubscriber} from '../OuterSubscriber'; +import { OuterSubscriber } from '../OuterSubscriber'; import { InnerSubscriber } from '../InnerSubscriber'; -import {subscribeToResult} from '../util/subscribeToResult'; -import {ObservableInput, OperatorFunction, MonoTypeOperatorFunction, ObservedValueOf} from '../types'; +import { subscribeToResult } from '../util/subscribeToResult'; +import { ObservableInput, OperatorFunction, ObservedValueOf } from '../types'; /* tslint:disable:max-line-length */ export function catchError>(selector: (err: any, caught: Observable) => O): OperatorFunction>; @@ -19,13 +19,13 @@ export function catchError>(selector: (err: an * ## Examples * Continues with a different Observable when there's an error * - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { map, catchError } from 'rxjs/operators'; * * of(1, 2, 3, 4, 5).pipe( * map(n => { - * if (n == 4) { + * if (n === 4) { * throw 'four!'; * } * return n; @@ -38,7 +38,7 @@ export function catchError>(selector: (err: an * * Retries the caught source Observable again in case of error, similar to retry() operator * - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { map, catchError, take } from 'rxjs/operators'; * @@ -58,13 +58,13 @@ export function catchError>(selector: (err: an * * Throws a new error when the source Observable throws an error * - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { map, catchError } from 'rxjs/operators'; * * of(1, 2, 3, 4, 5).pipe( * map(n => { - * if (n == 4) { + * if (n === 4) { * throw 'four!'; * } * return n; diff --git a/src/internal/operators/combineAll.ts b/src/internal/operators/combineAll.ts index 31e1932001..f696000341 100644 --- a/src/internal/operators/combineAll.ts +++ b/src/internal/operators/combineAll.ts @@ -23,10 +23,12 @@ export function combineAll(project: (...values: Array) => R): OperatorFu * --- * * ## Examples + * * ### Map two click events to a finite interval Observable, then apply `combineAll` - * ```javascript + * + * ```ts + * import { fromEvent, interval } from 'rxjs'; * import { map, combineAll, take } from 'rxjs/operators'; - * import { fromEvent } from 'rxjs/observable/fromEvent'; * * const clicks = fromEvent(document, 'click'); * const higherOrder = clicks.pipe( diff --git a/src/internal/operators/concatAll.ts b/src/internal/operators/concatAll.ts index d29acf8b84..a6716bb5cc 100644 --- a/src/internal/operators/concatAll.ts +++ b/src/internal/operators/concatAll.ts @@ -30,9 +30,9 @@ export function concatAll(): OperatorFunction; * ## Example * * For each click event, tick every second from 0 to 3, with no concurrency - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; - * import { ma, take, concatAll } from 'rxjs/operators'; + * import { map, take, concatAll } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); * const higherOrder = clicks.pipe( diff --git a/src/internal/operators/concatMap.ts b/src/internal/operators/concatMap.ts index 8f9108e6d0..64b0ba0f83 100644 --- a/src/internal/operators/concatMap.ts +++ b/src/internal/operators/concatMap.ts @@ -35,13 +35,13 @@ export function concatMap>(project: (value: * ## Example * For each click event, tick every second from 0 to 3, with no concurrency * - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { concatMap, take } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); * const result = clicks.pipe( - * concatMap(ev => interval(1000).pipe(take(4)), + * concatMap(ev => interval(1000).pipe(take(4))) * ); * result.subscribe(x => console.log(x)); * diff --git a/src/internal/operators/concatMapTo.ts b/src/internal/operators/concatMapTo.ts index 536351f4cf..3f52fc68b4 100644 --- a/src/internal/operators/concatMapTo.ts +++ b/src/internal/operators/concatMapTo.ts @@ -34,7 +34,7 @@ export function concatMapTo>(observable: O, * * ## Example * For each click event, tick every second from 0 to 3, with no concurrency - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { concatMapTo, take } from 'rxjs/operators'; * diff --git a/src/internal/operators/count.ts b/src/internal/operators/count.ts index bb631292b3..1c33a9a4c0 100644 --- a/src/internal/operators/count.ts +++ b/src/internal/operators/count.ts @@ -23,7 +23,7 @@ import { Subscriber } from '../Subscriber'; * ## Examples * * Counts how many seconds have passed before the first click happened - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { count, takeUntil } from 'rxjs/operators'; * @@ -35,7 +35,7 @@ import { Subscriber } from '../Subscriber'; * ``` * * Counts how many odd numbers are there between 1 and 7 - * ```javascript + * ```ts * import { range } from 'rxjs'; * import { count } from 'rxjs/operators'; * diff --git a/src/internal/operators/debounce.ts b/src/internal/operators/debounce.ts index e7c36b799b..be2167d9bf 100644 --- a/src/internal/operators/debounce.ts +++ b/src/internal/operators/debounce.ts @@ -33,7 +33,7 @@ import { subscribeToResult } from '../util/subscribeToResult'; * * ## Example * Emit the most recent click after a burst of clicks - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { debounce } from 'rxjs/operators'; * diff --git a/src/internal/operators/debounceTime.ts b/src/internal/operators/debounceTime.ts index 2a5505efbf..8244d49abc 100644 --- a/src/internal/operators/debounceTime.ts +++ b/src/internal/operators/debounceTime.ts @@ -30,7 +30,7 @@ import { MonoTypeOperatorFunction, SchedulerLike, TeardownLogic } from '../types * * ## Example * Emit the most recent click after a burst of clicks - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { debounceTime } from 'rxjs/operators'; * diff --git a/src/internal/operators/defaultIfEmpty.ts b/src/internal/operators/defaultIfEmpty.ts index 77ba726b77..6d6123ea58 100644 --- a/src/internal/operators/defaultIfEmpty.ts +++ b/src/internal/operators/defaultIfEmpty.ts @@ -23,7 +23,7 @@ export function defaultIfEmpty(defaultValue?: R): OperatorFunction(delayDurationSelector: (value: T, index: number) => * * ## Example * Delay each click by a random amount of time, between 0 and 5 seconds - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { delayWhen } from 'rxjs/operators'; * diff --git a/src/internal/operators/dematerialize.ts b/src/internal/operators/dematerialize.ts index 97ebe1bad7..ba1dcb17ef 100644 --- a/src/internal/operators/dematerialize.ts +++ b/src/internal/operators/dematerialize.ts @@ -23,7 +23,7 @@ import { OperatorFunction } from '../types'; * * ## Example * Convert an Observable of Notifications to an actual Observable - * ```javascript + * ```ts * import { of, Notification } from 'rxjs'; * import { dematerialize } from 'rxjs/operators'; * diff --git a/src/internal/operators/distinct.ts b/src/internal/operators/distinct.ts index 910a3eba88..b464420fca 100644 --- a/src/internal/operators/distinct.ts +++ b/src/internal/operators/distinct.ts @@ -22,7 +22,7 @@ import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; * * ## Examples * A simple example with numbers - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { distinct } from 'rxjs/operators'; * diff --git a/src/internal/operators/distinctUntilChanged.ts b/src/internal/operators/distinctUntilChanged.ts index 541db7ae30..9065941078 100644 --- a/src/internal/operators/distinctUntilChanged.ts +++ b/src/internal/operators/distinctUntilChanged.ts @@ -11,13 +11,16 @@ export function distinctUntilChanged(compare: (x: K, y: K) => boolean, key /** * Returns an Observable that emits all items emitted by the source Observable that are distinct by comparison from the previous item. * - * If a comparator function is provided, then it will be called for each item to test for whether or not that value should be emitted. + * It's like {@link filter}, but just emits the values that are distinct from the previous. + * + * ![](distinctUntilChanged.png) * + * If a comparator function is provided, then it will be called for each item to test for whether or not that value should be emitted. * If a comparator function is not provided, an equality check is used by default. * * ## Example * A simple example with numbers - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { distinctUntilChanged } from 'rxjs/operators'; * diff --git a/src/internal/operators/distinctUntilKeyChanged.ts b/src/internal/operators/distinctUntilKeyChanged.ts index b649fc5a92..fcf3a5e490 100644 --- a/src/internal/operators/distinctUntilKeyChanged.ts +++ b/src/internal/operators/distinctUntilKeyChanged.ts @@ -10,9 +10,14 @@ export function distinctUntilKeyChanged(key: K, compare: ( * Returns an Observable that emits all items emitted by the source Observable that are distinct by comparison from the previous item, * using a property accessed by using the key provided to check if the two items are distinct. * - * If a comparator function is provided, then it will be called for each item to test for whether or not that value should be emitted. + * It's like {@link distinctUntilChanged}, but the distinct comparison uses a key to access a property. * - * If a comparator function is not provided, an equality check is used by default. + * ![](distinctUntilKeyChanged.png) + * + * `distinctUntilKeyChanged` emits all items of the source Observable, wich are distinct by comparison. + * The comparison checks if the previous item is distinct from the current item, using a `key` to access a property. + * If a comparator function is provided, then it will be called for each item with the property key + * to test for whether or not that value should be emitted. * * ## Examples * An example comparing the name of persons diff --git a/src/internal/operators/elementAt.ts b/src/internal/operators/elementAt.ts index 05c78ed518..5cee9830ca 100644 --- a/src/internal/operators/elementAt.ts +++ b/src/internal/operators/elementAt.ts @@ -24,7 +24,7 @@ import { take } from './take'; * * ## Example * Emit only the third click event - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { elementAt } from 'rxjs/operators'; * diff --git a/src/internal/operators/endWith.ts b/src/internal/operators/endWith.ts index b380221aff..2619192067 100644 --- a/src/internal/operators/endWith.ts +++ b/src/internal/operators/endWith.ts @@ -1,19 +1,31 @@ import { Observable } from '../Observable'; -import { fromArray } from '../observable/fromArray'; -import { scalar } from '../observable/scalar'; -import { empty } from '../observable/empty'; -import { concat as concatStatic } from '../observable/concat'; -import { isScheduler } from '../util/isScheduler'; +import { concat } from '../observable/concat'; import { MonoTypeOperatorFunction, SchedulerLike, OperatorFunction } from '../types'; /* tslint:disable:max-line-length */ -export function endWith(scheduler?: SchedulerLike): MonoTypeOperatorFunction; -export function endWith(v1: A, scheduler?: SchedulerLike): OperatorFunction; -export function endWith(v1: A, v2: B, scheduler?: SchedulerLike): OperatorFunction; -export function endWith(v1: A, v2: B, v3: C, scheduler?: SchedulerLike): OperatorFunction; -export function endWith(v1: A, v2: B, v3: C, v4: D, scheduler?: SchedulerLike): OperatorFunction; -export function endWith(v1: A, v2: B, v3: C, v4: D, v5: E, scheduler?: SchedulerLike): OperatorFunction; -export function endWith(v1: A, v2: B, v3: C, v4: D, v5: E, v6: F, scheduler?: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */ +export function endWith(scheduler: SchedulerLike): MonoTypeOperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */ +export function endWith(v1: A, scheduler: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */ +export function endWith(v1: A, v2: B, scheduler: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */ +export function endWith(v1: A, v2: B, v3: C, scheduler: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */ +export function endWith(v1: A, v2: B, v3: C, v4: D, scheduler: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */ +export function endWith(v1: A, v2: B, v3: C, v4: D, v5: E, scheduler: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */ +export function endWith(v1: A, v2: B, v3: C, v4: D, v5: E, v6: F, scheduler: SchedulerLike): OperatorFunction; + +export function endWith(v1: A): OperatorFunction; +export function endWith(v1: A, v2: B): OperatorFunction; +export function endWith(v1: A, v2: B, v3: C): OperatorFunction; +export function endWith(v1: A, v2: B, v3: C, v4: D): OperatorFunction; +export function endWith(v1: A, v2: B, v3: C, v4: D, v5: E): OperatorFunction; +export function endWith(v1: A, v2: B, v3: C, v4: D, v5: E, v6: F): OperatorFunction; +export function endWith(...array: Z[]): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([source, [a, b, c]], scheduler).pipe(concatAll())`) */ export function endWith(...array: Array): OperatorFunction; /* tslint:enable:max-line-length */ @@ -26,7 +38,7 @@ export function endWith(...array: Array): OperatorF * ## Example * ### After the source observable completes, appends an emission and then completes too. * - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { endWith } from 'rxjs/operators'; * @@ -50,21 +62,5 @@ export function endWith(...array: Array): OperatorF * @owner Observable */ export function endWith(...array: Array): MonoTypeOperatorFunction { - return (source: Observable) => { - let scheduler = array[array.length - 1]; - if (isScheduler(scheduler)) { - array.pop(); - } else { - scheduler = null; - } - - const len = array.length; - if (len === 1 && !scheduler) { - return concatStatic(source, scalar(array[0] as T)); - } else if (len > 0) { - return concatStatic(source, fromArray(array as T[], scheduler)); - } else { - return concatStatic(source, empty(scheduler)); - } - }; + return (source: Observable) => concat(source, ...(array as any[])) as Observable; } diff --git a/src/internal/operators/every.ts b/src/internal/operators/every.ts index 5b985748d9..5e2c9a6513 100644 --- a/src/internal/operators/every.ts +++ b/src/internal/operators/every.ts @@ -8,7 +8,7 @@ import { Observer, OperatorFunction } from '../types'; * * ## Example * A simple example emitting true if all elements are less than 5, false otherwise - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { every } from 'rxjs/operators'; * diff --git a/src/internal/operators/exhaust.ts b/src/internal/operators/exhaust.ts index 155e534f13..ad14b760d6 100644 --- a/src/internal/operators/exhaust.ts +++ b/src/internal/operators/exhaust.ts @@ -28,7 +28,7 @@ export function exhaust(): OperatorFunction; * * ## Example * Run a finite timer for each click, only if there is no currently active timer - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { exhaust, map, take } from 'rxjs/operators'; * diff --git a/src/internal/operators/exhaustMap.ts b/src/internal/operators/exhaustMap.ts index 50cc6267ea..acfeb5c089 100644 --- a/src/internal/operators/exhaustMap.ts +++ b/src/internal/operators/exhaustMap.ts @@ -37,13 +37,13 @@ export function exhaustMap(project: (value: T, index: number) => Observ * * ## Example * Run a finite timer for each click, only if there is no currently active timer - * ```javascript - * import { fromEvent, } from 'rxjs'; + * ```ts + * import { fromEvent, interval } from 'rxjs'; * import { exhaustMap, take } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); * const result = clicks.pipe( - * exhaustMap((ev) => interval(1000).pipe(take(5))), + * exhaustMap(ev => interval(1000).pipe(take(5))) * ); * result.subscribe(x => console.log(x)); * ``` diff --git a/src/internal/operators/expand.ts b/src/internal/operators/expand.ts index de5ddadbee..b686d61573 100644 --- a/src/internal/operators/expand.ts +++ b/src/internal/operators/expand.ts @@ -34,7 +34,7 @@ export function expand(project: (value: T, index: number) => ObservableInput< * * ## Example * Start emitting the powers of two on every click, at most 10 of them - * ```javascript + * ```ts * import { fromEvent, of } from 'rxjs'; * import { expand, mapTo, delay, take } from 'rxjs/operators'; * diff --git a/src/internal/operators/filter.ts b/src/internal/operators/filter.ts index 8946dad927..953aacc3b2 100644 --- a/src/internal/operators/filter.ts +++ b/src/internal/operators/filter.ts @@ -26,7 +26,7 @@ export function filter(predicate: (value: T, index: number) => boolean, * * ## Example * Emit only click events whose target was a DIV element - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { filter } from 'rxjs/operators'; * diff --git a/src/internal/operators/find.ts b/src/internal/operators/find.ts index 1f0bdb2ebf..31cd7cc8d0 100644 --- a/src/internal/operators/find.ts +++ b/src/internal/operators/find.ts @@ -23,7 +23,7 @@ export function find(predicate: (value: T, index: number, source: Observable< * * ## Example * Find and emit the first click that happens on a DIV element - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { find } from 'rxjs/operators'; * diff --git a/src/internal/operators/findIndex.ts b/src/internal/operators/findIndex.ts index 5924e7c41a..bdb62d45ca 100644 --- a/src/internal/operators/findIndex.ts +++ b/src/internal/operators/findIndex.ts @@ -18,7 +18,7 @@ import { OperatorFunction } from '../types'; * * ## Example * Emit the index of first click that happens on a DIV element - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { findIndex } from 'rxjs/operators'; * diff --git a/src/internal/operators/first.ts b/src/internal/operators/first.ts index eb8808152c..80ad111271 100644 --- a/src/internal/operators/first.ts +++ b/src/internal/operators/first.ts @@ -43,7 +43,7 @@ export function first( * * ## Examples * Emit only the first click that happens on the DOM - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { first } from 'rxjs/operators'; * @@ -53,7 +53,7 @@ export function first( * ``` * * Emits the first click that happens on a DIV - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { first } from 'rxjs/operators'; * diff --git a/src/internal/operators/groupBy.ts b/src/internal/operators/groupBy.ts index 02ea119c9a..3f6343fcf1 100644 --- a/src/internal/operators/groupBy.ts +++ b/src/internal/operators/groupBy.ts @@ -31,22 +31,19 @@ export function groupBy(keySelector: (value: T) => K, elementSelector?: * returned by the elementSelector function. * * ## Examples + * * ### Group objects by id and return as array - * ```javascript - * import { mergeMap, groupBy, reduce } from 'rxjs/operators'; - * import { of } from 'rxjs/observable/of'; * - * interface Obj { - * id: number, - * name: string, - * } + * ```ts + * import { of } from 'rxjs'; + * import { mergeMap, groupBy, reduce } from 'rxjs/operators'; * - * of( - * {id: 1, name: 'javascript'}, - * {id: 2, name: 'parcel'}, + * of( + * {id: 1, name: 'JavaScript'}, + * {id: 2, name: 'Parcel'}, * {id: 2, name: 'webpack'}, - * {id: 1, name: 'typescript'}, - * {id: 3, name: 'tslint'} + * {id: 1, name: 'TypeScript'}, + * {id: 3, name: 'TSLint'} * ).pipe( * groupBy(p => p.id), * mergeMap((group$) => group$.pipe(reduce((acc, cur) => [...acc, cur], []))), @@ -54,37 +51,41 @@ export function groupBy(keySelector: (value: T) => K, elementSelector?: * .subscribe(p => console.log(p)); * * // displays: - * // [ { id: 1, name: 'javascript'}, - * // { id: 1, name: 'typescript'} ] + * // [ { id: 1, name: 'JavaScript'}, + * // { id: 1, name: 'TypeScript'} ] * // - * // [ { id: 2, name: 'parcel'}, + * // [ { id: 2, name: 'Parcel'}, * // { id: 2, name: 'webpack'} ] * // - * // [ { id: 3, name: 'tslint'} ] + * // [ { id: 3, name: 'TSLint'} ] * ``` * * ### Pivot data on the id field - * ```javascript - * import { mergeMap, groupBy, map, reduce } from 'rxjs/operators'; - * import { of } from 'rxjs/observable/of'; * - * of( - * {id: 1, name: 'javascript'}, - * {id: 2, name: 'parcel'}, - * {id: 2, name: 'webpack'}, - * {id: 1, name: 'typescript'} - * {id: 3, name: 'tslint'} - * ).pipe( - * groupBy(p => p.id, p => p.name), - * mergeMap( (group$) => group$.pipe(reduce((acc, cur) => [...acc, cur], ["" + group$.key]))), - * map(arr => ({'id': parseInt(arr[0]), 'values': arr.slice(1)})), + * ```ts + * import { of } from 'rxjs'; + * import { groupBy, map, mergeMap, reduce } from 'rxjs/operators'; + * + * of( + * { id: 1, name: 'JavaScript' }, + * { id: 2, name: 'Parcel' }, + * { id: 2, name: 'webpack' }, + * { id: 1, name: 'TypeScript' }, + * { id: 3, name: 'TSLint' } * ) - * .subscribe(p => console.log(p)); + * .pipe( + * groupBy(p => p.id, p => p.name), + * mergeMap(group$ => + * group$.pipe(reduce((acc, cur) => [...acc, cur], [`${group$.key}`])) + * ), + * map(arr => ({ id: parseInt(arr[0], 10), values: arr.slice(1) })) + * ) + * .subscribe(p => console.log(p)); * * // displays: - * // { id: 1, values: [ 'javascript', 'typescript' ] } - * // { id: 2, values: [ 'parcel', 'webpack' ] } - * // { id: 3, values: [ 'tslint' ] } + * // { id: 1, values: [ 'JavaScript', 'TypeScript' ] } + * // { id: 2, values: [ 'Parcel', 'webpack' ] } + * // { id: 3, values: [ 'TSLint' ] } * ``` * * @param {function(value: T): K} keySelector A function that extracts the key diff --git a/src/internal/operators/ignoreElements.ts b/src/internal/operators/ignoreElements.ts index 9e8e32c890..e153e774d2 100644 --- a/src/internal/operators/ignoreElements.ts +++ b/src/internal/operators/ignoreElements.ts @@ -10,9 +10,9 @@ import { OperatorFunction } from '../types'; * * ## Examples * ### Ignores emitted values, reacts to observable's completion. - * ```javascript + * ```ts * import { of } from 'rxjs'; - * import { ifnoreElements } from 'rxjs/operators'; + * import { ignoreElements } from 'rxjs/operators'; * * of('you', 'talking', 'to', 'me').pipe( * ignoreElements(), diff --git a/src/internal/operators/isEmpty.ts b/src/internal/operators/isEmpty.ts index 997a39db80..37a0467867 100644 --- a/src/internal/operators/isEmpty.ts +++ b/src/internal/operators/isEmpty.ts @@ -3,6 +3,66 @@ import { Subscriber } from '../Subscriber'; import { Observable } from '../Observable'; import { OperatorFunction } from '../types'; +/** + * Emits false if the input observable emits any values, or emits true if the + * input observable completes without emitting any values. + * + * Tells whether any values are emitted by an observable + * + * ![](isEmpty.png) + * + * `isEmpty` transforms an Observable that emits values into an Observable that + * emits a single boolean value representing whether or not any values were + * emitted by the source Observable. As soon as the source Observable emits a + * value, `isEmpty` will emit a `false` and complete. If the source Observable + * completes having not emitted anything, `isEmpty` will emit a `true` and + * complete. + * + * A similar effect could be achieved with {@link count}, but `isEmpty` can emit + * a `false` value sooner. + * + * ## Examples + * + * Emit `false` for a non-empty Observable + * ```javascript + * import { Subject } from 'rxjs'; + * import { isEmpty } from 'rxjs/operators'; + * + * const source = new Subject(); + * const result = source.pipe(isEmpty()); + * source.subscribe(x => console.log(x)); + * result.subscribe(x => console.log(x)); + * source.next('a'); + * source.next('b'); + * source.next('c'); + * source.complete(); + * + * // Results in: + * // a + * // false + * // b + * // c + * ``` + * + * Emit `true` for an empty Observable + * ```javascript + * import { EMPTY } from 'rxjs'; + * import { isEmpty } from 'rxjs/operators'; + * + * const result = EMPTY.pipe(isEmpty()); + * result.subscribe(x => console.log(x)); + * // Results in: + * // true + * ``` + * + * @see {@link count} + * @see {@link EMPTY} + * + * @return {OperatorFunction} An Observable of a boolean value indicating whether observable was empty or not + * @method isEmpty + * @owner Observable + */ + export function isEmpty(): OperatorFunction { return (source: Observable) => source.lift(new IsEmptyOperator()); } diff --git a/src/internal/operators/map.ts b/src/internal/operators/map.ts index 60f3c316c2..debc6be7b9 100644 --- a/src/internal/operators/map.ts +++ b/src/internal/operators/map.ts @@ -19,7 +19,7 @@ import { OperatorFunction } from '../types'; * * ## Example * Map every click to the clientX position of that click - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { map } from 'rxjs/operators'; * diff --git a/src/internal/operators/mapTo.ts b/src/internal/operators/mapTo.ts index c9d1f55348..a072604d4d 100644 --- a/src/internal/operators/mapTo.ts +++ b/src/internal/operators/mapTo.ts @@ -18,7 +18,7 @@ import { OperatorFunction } from '../types'; * * ## Example * Map every click to the string 'Hi' - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { mapTo } from 'rxjs/operators'; * diff --git a/src/internal/operators/materialize.ts b/src/internal/operators/materialize.ts index a9e3b3b205..deff345e3f 100644 --- a/src/internal/operators/materialize.ts +++ b/src/internal/operators/materialize.ts @@ -28,7 +28,7 @@ import { OperatorFunction } from '../types'; * * ## Example * Convert a faulty Observable to an Observable of Notifications - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { materialize, map } from 'rxjs/operators'; * diff --git a/src/internal/operators/max.ts b/src/internal/operators/max.ts index f06be55e67..d135aa6d83 100644 --- a/src/internal/operators/max.ts +++ b/src/internal/operators/max.ts @@ -9,7 +9,7 @@ import { MonoTypeOperatorFunction } from '../types'; * * ## Examples * Get the maximal value of a series of numbers - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { max } from 'rxjs/operators'; * diff --git a/src/internal/operators/mergeAll.ts b/src/internal/operators/mergeAll.ts index 0b9ba7f92e..6659290fad 100644 --- a/src/internal/operators/mergeAll.ts +++ b/src/internal/operators/mergeAll.ts @@ -20,7 +20,7 @@ import { OperatorFunction, ObservableInput } from '../types'; * * ## Examples * Spawn a new interval Observable for each click event, and blend their outputs as one Observable - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { map, mergeAll } from 'rxjs/operators'; * @@ -31,7 +31,7 @@ import { OperatorFunction, ObservableInput } from '../types'; * ``` * * Count from 0 to 9 every second for each click, but only allow 2 concurrent timers - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { take, map, mergeAll } from 'rxjs/operators'; * diff --git a/src/internal/operators/mergeMap.ts b/src/internal/operators/mergeMap.ts index 3414fe4af3..cf69688740 100644 --- a/src/internal/operators/mergeMap.ts +++ b/src/internal/operators/mergeMap.ts @@ -33,7 +33,7 @@ export function mergeMap>(project: (value: * * ## Example * Map and flatten each letter to an Observable ticking every 1 second - * ```javascript + * ```ts * import { of, interval } from 'rxjs'; * import { mergeMap, map } from 'rxjs/operators'; * diff --git a/src/internal/operators/mergeMapTo.ts b/src/internal/operators/mergeMapTo.ts index be74fe7fef..6f7e62fb66 100644 --- a/src/internal/operators/mergeMapTo.ts +++ b/src/internal/operators/mergeMapTo.ts @@ -24,7 +24,7 @@ export function mergeMapTo>(innerObservable * * ## Example * For each click event, start an interval Observable ticking every 1 second - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { mergeMapTo } from 'rxjs/operators'; * diff --git a/src/internal/operators/mergeScan.ts b/src/internal/operators/mergeScan.ts index dfd1916588..7d12345e84 100644 --- a/src/internal/operators/mergeScan.ts +++ b/src/internal/operators/mergeScan.ts @@ -17,9 +17,9 @@ import { ObservableInput, OperatorFunction } from '../types'; * * ## Example * Count the number of click events - * ```javascript + * ```ts * import { fromEvent, of } from 'rxjs'; - * import { mapTo } from 'rxjs/operators'; + * import { mapTo, mergeScan } from 'rxjs/operators'; * * const click$ = fromEvent(document, 'click'); * const one$ = click$.pipe(mapTo(1)); @@ -30,10 +30,10 @@ import { ObservableInput, OperatorFunction } from '../types'; * count$.subscribe(x => console.log(x)); * * // Results: - * 1 - * 2 - * 3 - * 4 + * // 1 + * // 2 + * // 3 + * // 4 * // ...and so on for each click * ``` * diff --git a/src/internal/operators/min.ts b/src/internal/operators/min.ts index db63f8c8ee..c7c0e2ccf1 100644 --- a/src/internal/operators/min.ts +++ b/src/internal/operators/min.ts @@ -9,7 +9,7 @@ import { MonoTypeOperatorFunction } from '../types'; * * ## Examples * Get the minimal value of a series of numbers - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { min } from 'rxjs/operators'; * diff --git a/src/internal/operators/observeOn.ts b/src/internal/operators/observeOn.ts index 4d241d0d12..8a5bd66499 100644 --- a/src/internal/operators/observeOn.ts +++ b/src/internal/operators/observeOn.ts @@ -33,7 +33,7 @@ import { MonoTypeOperatorFunction, PartialObserver, SchedulerAction, SchedulerLi * * ## Example * Ensure values in subscribe are called just before browser repaint. - * ```javascript + * ```ts * import { interval } from 'rxjs'; * import { observeOn } from 'rxjs/operators'; * diff --git a/src/internal/operators/onErrorResumeNext.ts b/src/internal/operators/onErrorResumeNext.ts index 5ff8e2d53d..1138d3e52a 100644 --- a/src/internal/operators/onErrorResumeNext.ts +++ b/src/internal/operators/onErrorResumeNext.ts @@ -14,7 +14,7 @@ export function onErrorResumeNext(v: ObservableInput): OperatorFunction export function onErrorResumeNext(v2: ObservableInput, v3: ObservableInput): OperatorFunction; export function onErrorResumeNext(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput): OperatorFunction; export function onErrorResumeNext(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput): OperatorFunction; -export function onErrorResumeNext(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput): OperatorFunction ; +export function onErrorResumeNext(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput): OperatorFunction; export function onErrorResumeNext(...observables: Array | ((...values: Array) => R)>): OperatorFunction; export function onErrorResumeNext(array: ObservableInput[]): OperatorFunction; /* tslint:enable:max-line-length */ @@ -51,7 +51,7 @@ export function onErrorResumeNext(array: ObservableInput[]): Operator * * ## Example * Subscribe to the next Observable after map fails - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { onErrorResumeNext, map } from 'rxjs/operators'; * @@ -89,8 +89,7 @@ export function onErrorResumeNext(array: ObservableInput[]): Operator */ export function onErrorResumeNext(...nextSources: Array | - Array> | - ((...values: Array) => R)>): OperatorFunction { + Array>>): OperatorFunction { if (nextSources.length === 1 && isArray(nextSources[0])) { nextSources = >>nextSources[0]; } @@ -110,8 +109,8 @@ export function onErrorResumeNextStatic(array: ObservableInput[]): Obser /* tslint:enable:max-line-length */ export function onErrorResumeNextStatic(...nextSources: Array | - Array> | - ((...values: Array) => R)>): Observable { + Array> | + ((...values: Array) => R)>): Observable { let source: ObservableInput = null; if (nextSources.length === 1 && isArray(nextSources[0])) { diff --git a/src/internal/operators/pairwise.ts b/src/internal/operators/pairwise.ts index 2fee6a962d..209f90fad2 100644 --- a/src/internal/operators/pairwise.ts +++ b/src/internal/operators/pairwise.ts @@ -20,7 +20,7 @@ import { OperatorFunction } from '../types'; * * ## Example * On every click (starting from the second), emit the relative distance to the previous click - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { pairwise, map } from 'rxjs/operators'; * diff --git a/src/internal/operators/partition.ts b/src/internal/operators/partition.ts index d2f06e8b86..41c00c4807 100644 --- a/src/internal/operators/partition.ts +++ b/src/internal/operators/partition.ts @@ -22,7 +22,7 @@ import { UnaryFunction } from '../types'; * * ## Example * Partition click events into those on DIV elements and those elsewhere - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { partition } from 'rxjs/operators'; * @@ -50,6 +50,9 @@ import { UnaryFunction } from '../types'; * @method partition * @owner Observable */ +/** + * @deprecated use `partition` static creation function instead + */ export function partition(predicate: (value: T, index: number) => boolean, thisArg?: any): UnaryFunction, [Observable, Observable]> { return (source: Observable) => [ diff --git a/src/internal/operators/pluck.ts b/src/internal/operators/pluck.ts index f687629ecf..dbe050f25d 100644 --- a/src/internal/operators/pluck.ts +++ b/src/internal/operators/pluck.ts @@ -27,7 +27,7 @@ export function pluck(...properties: string[]): OperatorFunction; * * ## Example * Map every click to the tagName of the clicked target element - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { pluck } from 'rxjs/operators'; * diff --git a/src/internal/operators/publish.ts b/src/internal/operators/publish.ts index 4678790393..e4596f30da 100644 --- a/src/internal/operators/publish.ts +++ b/src/internal/operators/publish.ts @@ -21,36 +21,34 @@ export function publish(selector: MonoTypeOperatorFunction): MonoTypeOpera * ## Examples * Make source$ hot by applying publish operator, then merge each inner observable into a single one * and subscribe. - * ```typescript + * ```ts * import { of, zip, interval, merge } from "rxjs"; - * import { map, publish } from "rxjs/operators"; + * import { map, publish, tap } from "rxjs/operators"; * - * const source$ = zip( - * interval(2000), - * of(1, 2, 3, 4, 5, 6, 7, 8, 9), - * ).pipe( - * map(values => values[1]) - * ); + * const source$ = zip(interval(2000), of(1, 2, 3, 4, 5, 6, 7, 8, 9)).pipe( + * map(values => values[1]) + * ); * - * source$.pipe( - * publish(multicasted$ => { - * return merge( - * multicasted$.pipe(tap(x => console.log('Stream 1:', x))), - * multicasted$.pipe(tap(x => console.log('Stream 2:', x))), - * multicasted$.pipe(tap(x => console.log('Stream 3:', x))), - * ); - * })).subscribe(); + * source$ + * .pipe( + * publish(multicasted$ => + * merge( + * multicasted$.pipe(tap(x => console.log('Stream 1:', x))), + * multicasted$.pipe(tap(x => console.log('Stream 2:', x))), + * multicasted$.pipe(tap(x => console.log('Stream 3:', x))), + * ) + * ) + * ) + * .subscribe(); * - /* Results every two seconds - * Stream 1: 1 - * Stream 2: 1 - * Stream 3: 1 - * - * ... - * - * Stream 1: 9 - * Stream 2: 9 - * Stream 3: 9 + * // Results every two seconds + * // Stream 1: 1 + * // Stream 2: 1 + * // Stream 3: 1 + * // ... + * // Stream 1: 9 + * // Stream 2: 9 + * // Stream 3: 9 * ``` * * @param {Function} [selector] - Optional selector function which can use the multicasted source sequence as many times diff --git a/src/internal/operators/publishLast.ts b/src/internal/operators/publishLast.ts index 98c8fda990..e431657cb4 100644 --- a/src/internal/operators/publishLast.ts +++ b/src/internal/operators/publishLast.ts @@ -18,7 +18,7 @@ import { UnaryFunction } from '../types'; * * ## Example * - * ```javascript + * ```ts * import { interval } from 'rxjs'; * import { publishLast, tap, take } from 'rxjs/operators'; * diff --git a/src/internal/operators/race.ts b/src/internal/operators/race.ts index 5cefbf0c3f..75b51d349f 100644 --- a/src/internal/operators/race.ts +++ b/src/internal/operators/race.ts @@ -15,8 +15,8 @@ export function race(...observables: Array | Array(accumulator: (acc: R, value: T, index: number) => R * * ## Example * Count the number of click events that happened in 5 seconds - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { reduce, takeUntil, mapTo } from 'rxjs/operators'; * diff --git a/src/internal/operators/repeat.ts b/src/internal/operators/repeat.ts index 70f9ecceec..7bde309a69 100644 --- a/src/internal/operators/repeat.ts +++ b/src/internal/operators/repeat.ts @@ -5,14 +5,58 @@ import { empty } from '../observable/empty'; import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; /** - * Returns an Observable that repeats the stream of items emitted by the source Observable at most count times. + * Returns an Observable that will resubscribe to the source stream when the source stream completes, at most count times. + * + * Repeats all values emitted on the source. It's like {@link retry}, but for non error cases. * * ![](repeat.png) * + * Similar to {@link retry}, this operator repeats the stream of items emitted by the source for non error cases. + * Repeat can be useful for creating observables that are meant to have some repeated pattern or rhythm. + * + * Note: `repeat(0)` returns an empty observable and `repeat()` will repeat forever + * + * ## Example + * Repeat a message stream + * ```ts + * import { of } from 'rxjs'; + * import { repeat, delay } from 'rxjs/operators'; + * + * const source = of('Repeat message'); + * const example = source.pipe(repeat(3)); + * example.subscribe(x => console.log(x)); + * + * // Results + * // Repeat message + * // Repeat message + * // Repeat message + * ``` + * + * Repeat 3 values, 2 times + * ```ts + * import { interval } from 'rxjs'; + * import { repeat, take } from 'rxjs/operators'; + * + * const source = interval(1000); + * const example = source.pipe(take(3), repeat(2)); + * example.subscribe(x => console.log(x)); + * + * // Results every second + * // 0 + * // 1 + * // 2 + * // 0 + * // 1 + * // 2 + * ``` + * + * @see {@link repeatWhen} + * @see {@link retry} + * * @param {number} [count] The number of times the source Observable items are repeated, a count of 0 will yield * an empty Observable. - * @return {Observable} An Observable that repeats the stream of items emitted by the source Observable at most - * count times. + * @return {Observable} An Observable that will resubscribe to the source stream when the source stream completes + * , at most count times. * @method repeat * @owner Observable */ diff --git a/src/internal/operators/repeatWhen.ts b/src/internal/operators/repeatWhen.ts index cd498ef9c5..ecdbe3112e 100644 --- a/src/internal/operators/repeatWhen.ts +++ b/src/internal/operators/repeatWhen.ts @@ -18,6 +18,22 @@ import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; * * ![](repeatWhen.png) * + * ## Example + * Repeat a message stream on click + * ```ts + * import { of, fromEvent } from 'rxjs'; + * import { repeatWhen } from 'rxjs/operators'; + * + * const source = of('Repeat message'); + * const documentClick$ = fromEvent(document, 'click'); + * + * source.pipe(repeatWhen(() => documentClick$) + * ).subscribe(data => console.log(data)) + * ``` + * @see {@link repeat} + * @see {@link retry} + * @see {@link retryWhen} + * * @param {function(notifications: Observable): Observable} notifier - Receives an Observable of notifications with * which a user can `complete` or `error`, aborting the repetition. * @return {Observable} The source Observable modified with repeat logic. diff --git a/src/internal/operators/sample.ts b/src/internal/operators/sample.ts index 70ced5b7e3..5665dec1ba 100644 --- a/src/internal/operators/sample.ts +++ b/src/internal/operators/sample.ts @@ -24,7 +24,7 @@ import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; * * ## Example * On every click, sample the most recent "seconds" timer - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { sample } from 'rxjs/operators'; * diff --git a/src/internal/operators/sampleTime.ts b/src/internal/operators/sampleTime.ts index 632b29ed11..0ff12275ff 100644 --- a/src/internal/operators/sampleTime.ts +++ b/src/internal/operators/sampleTime.ts @@ -22,7 +22,7 @@ import { MonoTypeOperatorFunction, SchedulerAction, SchedulerLike, TeardownLogic * * ## Example * Every second, emit the most recent click at most once - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { sampleTime } from 'rxjs/operators'; * diff --git a/src/internal/operators/scan.ts b/src/internal/operators/scan.ts index d5bfc45aca..f3b4d03470 100644 --- a/src/internal/operators/scan.ts +++ b/src/internal/operators/scan.ts @@ -30,7 +30,7 @@ export function scan(accumulator: (acc: R, value: T, index: number) => R, * * ## Example * Count the number of click events - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { scan, mapTo } from 'rxjs/operators'; * diff --git a/src/internal/operators/sequenceEqual.ts b/src/internal/operators/sequenceEqual.ts index de198b3357..a614c49383 100644 --- a/src/internal/operators/sequenceEqual.ts +++ b/src/internal/operators/sequenceEqual.ts @@ -23,7 +23,7 @@ import { Observer, OperatorFunction } from '../types'; * * ## Example * figure out if the Konami code matches - * ```javascript + * ```ts * import { from, fromEvent } from 'rxjs'; * import { sequenceEqual, bufferCount, mergeMap, map } from 'rxjs/operators'; * diff --git a/src/internal/operators/shareReplay.ts b/src/internal/operators/shareReplay.ts index 263b98f39d..30788a0871 100644 --- a/src/internal/operators/shareReplay.ts +++ b/src/internal/operators/shareReplay.ts @@ -29,7 +29,7 @@ export interface ShareReplayConfig { * ![](shareReplay.png) * * ## Example - * ```javascript + * ```ts * import { interval } from 'rxjs'; * import { shareReplay, take } from 'rxjs/operators'; * diff --git a/src/internal/operators/single.ts b/src/internal/operators/single.ts index de7642cc25..e23e1473bc 100644 --- a/src/internal/operators/single.ts +++ b/src/internal/operators/single.ts @@ -11,8 +11,37 @@ import { Observer, MonoTypeOperatorFunction, TeardownLogic } from '../types'; * items, notify of an IllegalArgumentException or NoSuchElementException respectively. If the source Observable * emits items but none match the specified predicate then `undefined` is emitted. * + * Like {@link first}, but emit with error notification if there is more than one value. * ![](single.png) * + * ## Example + * emits 'error' + * ```ts + * import { range } from 'rxjs'; + * import { single } from 'rxjs/operators'; + * + * const numbers = range(1,5).pipe(single()); + * numbers.subscribe(x => console.log('never get called'), e => console.log('error')); + * // result + * // 'error' + * ``` + * + * emits 'undefined' + * ```ts + * import { range } from 'rxjs'; + * import { single } from 'rxjs/operators'; + * + * const numbers = range(1,5).pipe(single(x => x === 10)); + * numbers.subscribe(x => console.log(x)); + * // result + * // 'undefined' + * ``` + * + * @see {@link first} + * @see {@link find} + * @see {@link findIndex} + * @see {@link elementAt} + * * @throws {EmptyError} Delivers an EmptyError to the Observer's `error` * callback if the Observable completes before any `next` notification was sent. * @param {Function} predicate - A predicate function to evaluate items emitted by the source Observable. diff --git a/src/internal/operators/skipLast.ts b/src/internal/operators/skipLast.ts index d7c280e8b5..60b6087531 100644 --- a/src/internal/operators/skipLast.ts +++ b/src/internal/operators/skipLast.ts @@ -16,7 +16,7 @@ import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; * * ## Example * Skip the last 2 values of an Observable with many values - * ```javascript + * ```ts * import { range } from 'rxjs'; * import { skipLast } from 'rxjs/operators'; * diff --git a/src/internal/operators/startWith.ts b/src/internal/operators/startWith.ts index d2ad09bf6a..81ce063213 100644 --- a/src/internal/operators/startWith.ts +++ b/src/internal/operators/startWith.ts @@ -1,19 +1,32 @@ import { Observable } from '../Observable'; -import { fromArray } from '../observable/fromArray'; -import { scalar } from '../observable/scalar'; -import { empty } from '../observable/empty'; -import { concat as concatStatic } from '../observable/concat'; +import { concat } from '../observable/concat'; import { isScheduler } from '../util/isScheduler'; import { MonoTypeOperatorFunction, OperatorFunction, SchedulerLike } from '../types'; /* tslint:disable:max-line-length */ -export function startWith(scheduler?: SchedulerLike): MonoTypeOperatorFunction; -export function startWith(v1: D, scheduler?: SchedulerLike): OperatorFunction; -export function startWith(v1: D, v2: E, scheduler?: SchedulerLike): OperatorFunction; -export function startWith(v1: D, v2: E, v3: F, scheduler?: SchedulerLike): OperatorFunction; -export function startWith(v1: D, v2: E, v3: F, v4: G, scheduler?: SchedulerLike): OperatorFunction; -export function startWith(v1: D, v2: E, v3: F, v4: G, v5: H, scheduler?: SchedulerLike): OperatorFunction; -export function startWith(v1: D, v2: E, v3: F, v4: G, v5: H, v6: I, scheduler?: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ +export function startWith(scheduler: SchedulerLike): MonoTypeOperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ +export function startWith(v1: D, scheduler: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ +export function startWith(v1: D, v2: E, scheduler: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ +export function startWith(v1: D, v2: E, v3: F, scheduler: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ +export function startWith(v1: D, v2: E, v3: F, v4: G, scheduler: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ +export function startWith(v1: D, v2: E, v3: F, v4: G, v5: H, scheduler: SchedulerLike): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ +export function startWith(v1: D, v2: E, v3: F, v4: G, v5: H, v6: I, scheduler: SchedulerLike): OperatorFunction; + +export function startWith(v1: D): OperatorFunction; +export function startWith(v1: D, v2: E): OperatorFunction; +export function startWith(v1: D, v2: E, v3: F): OperatorFunction; +export function startWith(v1: D, v2: E, v3: F, v4: G): OperatorFunction; +export function startWith(v1: D, v2: E, v3: F, v4: G, v5: H): OperatorFunction; +export function startWith(v1: D, v2: E, v3: F, v4: G, v5: H, v6: I): OperatorFunction; +export function startWith(...array: D[]): OperatorFunction; +/** @deprecated use {@link scheduled} and {@link concatAll} (e.g. `scheduled([[a, b, c], source], scheduler).pipe(concatAll())`) */ export function startWith(...array: Array): OperatorFunction; /* tslint:enable:max-line-length */ @@ -30,7 +43,7 @@ export function startWith(...array: Array): Operato * * Start the chain of emissions with `"first"`, `"second"` * - * ```javascript + * ```ts * import { of } from 'rxjs'; * import { startWith } from 'rxjs/operators'; * @@ -53,21 +66,12 @@ export function startWith(...array: Array): Operato * @owner Observable */ export function startWith(...array: Array): OperatorFunction { - return (source: Observable) => { - let scheduler = array[array.length - 1]; - if (isScheduler(scheduler)) { - array.pop(); - } else { - scheduler = null; - } - - const len = array.length; - if (len === 1 && !scheduler) { - return concatStatic(scalar(array[0] as T), source); - } else if (len > 0) { - return concatStatic(fromArray(array as T[], scheduler), source); - } else { - return concatStatic(empty(scheduler), source); - } - }; + const scheduler = array[array.length - 1] as SchedulerLike; + if (isScheduler(scheduler)) { + // deprecated path + array.pop(); + return (source: Observable) => concat(array as T[], source, scheduler); + } else { + return (source: Observable) => concat(array as T[], source); + } } diff --git a/src/internal/operators/subscribeOn.ts b/src/internal/operators/subscribeOn.ts index 2cae92d90f..3c5a4c1803 100644 --- a/src/internal/operators/subscribeOn.ts +++ b/src/internal/operators/subscribeOn.ts @@ -7,8 +7,39 @@ import { MonoTypeOperatorFunction, SchedulerLike, TeardownLogic } from '../types /** * Asynchronously subscribes Observers to this Observable on the specified {@link SchedulerLike}. * + * With `subscribeOn` you can decide what type of scheduler a specific Observable will be using when it is subscribed to. + * + * Schedulers control the speed and order of emissions to observers from an Observable stream. + * * ![](subscribeOn.png) * + * ## Example + * Given the following code: + * ```javascript + * import { of, merge } from 'rxjs'; + * + * const a = of(1, 2, 3, 4); + * const b = of(5, 6, 7, 8, 9); + * merge(a, b).subscribe(console.log); + * ``` + * + * Both Observable `a` and `b` will emit their values directly and synchronously once they are subscribed to. + * This will result in the output of `1 2 3 4 5 6 7 8 9`. + * + * But if we instead us the `subscribeOn` operator declaring that we want to use the {@link asyncScheduler} for values emited by Observable `a`: + * ```javascript + * import { of, merge, asyncScheduler } from 'rxjs'; + * import { subscribeOn } from 'rxjs/operators'; + * + * const a = of(1, 2, 3, 4).pipe(subscribeOn(asyncScheduler)); + * const b = of(5, 6, 7, 8, 9); + * merge(a, b).subscribe(console.log); + * ``` + * + * The output will instead be `5 6 7 8 9 1 2 3 4`. + * The reason for this is that Observable `b` emits its values directly and synchronously like before + * but the emissions from `a` are scheduled on the event loop because we are now using the {@link asyncScheduler} for that specific Observable. + * * @param {SchedulerLike} scheduler - The {@link SchedulerLike} to perform subscription actions on. * @return {Observable} The source Observable modified so that its subscriptions happen on the specified {@link SchedulerLike}. . diff --git a/src/internal/operators/switchAll.ts b/src/internal/operators/switchAll.ts index 1d895228fd..5fd82a4a0d 100644 --- a/src/internal/operators/switchAll.ts +++ b/src/internal/operators/switchAll.ts @@ -27,7 +27,7 @@ export function switchAll(): OperatorFunction; * * ```ts * import { fromEvent, interval } from 'rxjs'; - * import { switchAll, map } from 'rxjs/operators'; + * import { switchAll, map, tap } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click').pipe(tap(() => console.log('click'))); * const source = clicks.pipe(map((ev) => interval(1000))); @@ -62,4 +62,4 @@ export function switchAll(): OperatorFunction; export function switchAll(): OperatorFunction, T> { return switchMap(identity); -} \ No newline at end of file +} diff --git a/src/internal/operators/switchMap.ts b/src/internal/operators/switchMap.ts index 227a76b75f..b8b9cfb722 100644 --- a/src/internal/operators/switchMap.ts +++ b/src/internal/operators/switchMap.ts @@ -36,8 +36,24 @@ export function switchMap>(project: (value: * subsequent inner Observables. * * ## Example + * Generate new Observable according to source Observable values + * ```typescript + * import { of } from 'rxjs'; + * import { switchMap } from 'rxjs/operators'; + * + * const switched = of(1, 2, 3).pipe(switchMap((x: number) => of(x, x ** 2, x ** 3))); + * switched.subscribe(x => console.log(x)); + * // outputs + * // 1 + * // 1 + * // 2 + * // 4 + * // 8 + * // ... and so on + * ``` + * * Rerun an interval Observable on every click event - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { switchMap } from 'rxjs/operators'; * diff --git a/src/internal/operators/switchMapTo.ts b/src/internal/operators/switchMapTo.ts index 2c104ff2fb..84424744d4 100644 --- a/src/internal/operators/switchMapTo.ts +++ b/src/internal/operators/switchMapTo.ts @@ -33,7 +33,7 @@ export function switchMapTo(observable: ObservableInput, resultSelec * * ## Example * Rerun an interval Observable on every click event - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { switchMapTo } from 'rxjs/operators'; * diff --git a/src/internal/operators/take.ts b/src/internal/operators/take.ts index b03f106b5d..b43205c1cf 100644 --- a/src/internal/operators/take.ts +++ b/src/internal/operators/take.ts @@ -20,7 +20,7 @@ import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; * * ## Example * Take the first 5 seconds of an infinite 1-second interval Observable - * ```javascript + * ```ts * import { interval } from 'rxjs'; * import { take } from 'rxjs/operators'; * diff --git a/src/internal/operators/takeLast.ts b/src/internal/operators/takeLast.ts index e2d5e97b4a..5d215f64fe 100644 --- a/src/internal/operators/takeLast.ts +++ b/src/internal/operators/takeLast.ts @@ -23,7 +23,7 @@ import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; * * ## Example * Take the last 3 values of an Observable with many values - * ```javascript + * ```ts * import { range } from 'rxjs'; * import { takeLast } from 'rxjs/operators'; * diff --git a/src/internal/operators/takeUntil.ts b/src/internal/operators/takeUntil.ts index 28b2cc38d5..84ea1c7fbf 100644 --- a/src/internal/operators/takeUntil.ts +++ b/src/internal/operators/takeUntil.ts @@ -25,7 +25,7 @@ import { MonoTypeOperatorFunction, TeardownLogic } from '../types'; * * ## Example * Tick every second until the first click happens - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { takeUntil } from 'rxjs/operators'; * diff --git a/src/internal/operators/takeWhile.ts b/src/internal/operators/takeWhile.ts index f190e2a544..473169b182 100644 --- a/src/internal/operators/takeWhile.ts +++ b/src/internal/operators/takeWhile.ts @@ -26,7 +26,7 @@ export function takeWhile(predicate: (value: T, index: number) => boolean, in * * ## Example * Emit click events only while the clientX property is greater than 200 - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { takeWhile } from 'rxjs/operators'; * diff --git a/src/internal/operators/tap.ts b/src/internal/operators/tap.ts index 661d055eea..57087a0d8e 100644 --- a/src/internal/operators/tap.ts +++ b/src/internal/operators/tap.ts @@ -41,7 +41,7 @@ export function tap(observer: PartialObserver): MonoTypeOperatorFunction console.log(x)); * ``` * + * #### Double Click + * + * The following example only emits clicks which happen within a subsequent + * delay of 400ms of the previous click. This for example can emulate a double + * click. It makes use of the `trailing` parameter of the throttle configuration. + * + * ```ts + * import { fromEvent, asyncScheduler } from 'rxjs'; + * import { throttleTime, withLatestFrom } from 'rxjs/operators'; + * + * // defaultThottleConfig = { leading: true, trailing: false } + * const throttleConfig = { + * leading: false, + * trailing: true + * } + * + * const click = fromEvent(document, 'click'); + * const doubleClick = click.pipe( + * throttleTime(400, asyncScheduler, throttleConfig) + * ); + * + * doubleClick.subscribe((throttleValue: Event) => { + * console.log(`Double-clicked! Timestamp: ${throttleValue.timeStamp}`); + * }); + * ``` + * + * If you enable the `leading` parameter in this example, the output would be the primary click and + * the double click, but restricts additional clicks within 400ms. + * * @see {@link auditTime} * @see {@link debounceTime} * @see {@link delay} diff --git a/src/internal/operators/throwIfEmpty.ts b/src/internal/operators/throwIfEmpty.ts index 8f54dadd33..7f39c4e93f 100644 --- a/src/internal/operators/throwIfEmpty.ts +++ b/src/internal/operators/throwIfEmpty.ts @@ -1,6 +1,8 @@ -import { tap } from './tap'; import { EmptyError } from '../util/EmptyError'; -import { MonoTypeOperatorFunction } from '../types'; +import { Observable } from '../Observable'; +import { Operator } from '../Operator'; +import { Subscriber } from '../Subscriber'; +import { TeardownLogic, MonoTypeOperatorFunction } from '../types'; /** * If the source observable completes without emitting a value, it will emit @@ -10,38 +12,69 @@ import { MonoTypeOperatorFunction } from '../types'; * ![](throwIfEmpty.png) * * ## Example - * ```javascript + * ```ts * import { fromEvent, timer } from 'rxjs'; * import { throwIfEmpty, takeUntil } from 'rxjs/operators'; * - * const click$ = fromEvent(button, 'click'); + * const click$ = fromEvent(document, 'click'); * - * clicks$.pipe( + * click$.pipe( * takeUntil(timer(1000)), * throwIfEmpty( - * () => new Error('the button was not clicked within 1 second') + * () => new Error('the document was not clicked within 1 second') * ), * ) * .subscribe({ * next() { console.log('The button was clicked'); }, - * error(err) { console.error(err); }, + * error(err) { console.error(err); } * }); * ``` * - * @param {Function} [errorFactory] A factory function called to produce the + * @param errorFactory A factory function called to produce the * error to be thrown when the source observable completes without emitting a * value. */ -export const throwIfEmpty = - (errorFactory: (() => any) = defaultErrorFactory) => tap({ - hasValue: false, - next() { this.hasValue = true; }, - complete() { - if (!this.hasValue) { - throw errorFactory(); +export function throwIfEmpty (errorFactory: (() => any) = defaultErrorFactory): MonoTypeOperatorFunction { + return (source: Observable) => { + return source.lift(new ThrowIfEmptyOperator(errorFactory)); + }; +} + +class ThrowIfEmptyOperator implements Operator { + constructor(private errorFactory: () => any) { + } + + call(subscriber: Subscriber, source: any): TeardownLogic { + return source.subscribe(new ThrowIfEmptySubscriber(subscriber, this.errorFactory)); + } +} + +class ThrowIfEmptySubscriber extends Subscriber { + private hasValue: boolean = false; + + constructor(destination: Subscriber, private errorFactory: () => any) { + super(destination); + } + + protected _next(value: T): void { + this.hasValue = true; + this.destination.next(value); + } + + protected _complete() { + if (!this.hasValue) { + let err: any; + try { + err = this.errorFactory(); + } catch (e) { + err = e; } + this.destination.error(err); + } else { + return this.destination.complete(); } - } as any); + } +} function defaultErrorFactory() { return new EmptyError(); diff --git a/src/internal/operators/timeInterval.ts b/src/internal/operators/timeInterval.ts index 13e73b869c..00c3776138 100644 --- a/src/internal/operators/timeInterval.ts +++ b/src/internal/operators/timeInterval.ts @@ -21,7 +21,7 @@ import { map } from './map'; * ## Examples * Emit inteval between current value with the last value * - * ```javascript + * ```ts * const seconds = interval(1000); * * seconds.pipe(timeinterval()) diff --git a/src/internal/operators/timeout.ts b/src/internal/operators/timeout.ts index 5560739e04..b049e73001 100644 --- a/src/internal/operators/timeout.ts +++ b/src/internal/operators/timeout.ts @@ -39,7 +39,7 @@ import { throwError } from '../observable/throwError'; * * ## Examples * Check if ticks are emitted within certain timespan - * ```javascript + * ```ts * import { interval } from 'rxjs'; * import { timeout } from 'rxjs/operators'; * @@ -61,7 +61,7 @@ import { throwError } from '../observable/throwError'; * ``` * * Use Date to check if Observable completed - * ```javascript + * ```ts * import { interval } from 'rxjs'; * import { timeout } from 'rxjs/operators'; * diff --git a/src/internal/operators/timeoutWith.ts b/src/internal/operators/timeoutWith.ts index 6e4a3c5c53..753b16707c 100644 --- a/src/internal/operators/timeoutWith.ts +++ b/src/internal/operators/timeoutWith.ts @@ -39,7 +39,7 @@ export function timeoutWith(due: number | Date, withObservable: Observable * * ## Example * Add fallback observable - * ```javascript + * ```ts * import { intrerval } from 'rxjs'; * import { timeoutWith } from 'rxjs/operators'; * diff --git a/src/internal/operators/timestamp.ts b/src/internal/operators/timestamp.ts index cdfed176a6..d4061c8c45 100644 --- a/src/internal/operators/timestamp.ts +++ b/src/internal/operators/timestamp.ts @@ -4,6 +4,34 @@ import { OperatorFunction, SchedulerLike, Timestamp as TimestampInterface } from import { map } from './map'; /** + * Attaches a timestamp to each item emitted by an observable indicating when it was emitted + * + * The `timestamp` operator maps the *source* observable stream to an object of type + * `{value: T, timestamp: R}`. The properties are generically typed. The `value` property contains the value + * and type of the *source* observable. The `timestamp` is generated by the schedulers `now` function. By + * default it uses the *async* scheduler which simply returns `Date.now()` (milliseconds since 1970/01/01 + * 00:00:00:000) and therefore is of type `number`. + * + * ![](timestamp.png) + * + * ## Example + * + * In this example there is a timestamp attached to the documents click event. + * + * ```ts + * import { fromEvent } from 'rxjs'; + * import { timestamp } from 'rxjs/operators'; + * + * const clickWithTimestamp = fromEvent(document, 'click').pipe( + * timestamp() + * ); + * + * // Emits data of type {value: MouseEvent, timestamp: number} + * clickWithTimestamp.subscribe(data => { + * console.log(data); + * }); + * ``` + * * @param scheduler * @return {Observable>|WebSocketSubject|Observable} * @method timestamp diff --git a/src/internal/operators/toArray.ts b/src/internal/operators/toArray.ts index 160da34202..c1f1896398 100644 --- a/src/internal/operators/toArray.ts +++ b/src/internal/operators/toArray.ts @@ -10,5 +10,5 @@ function toArrayReducer(arr: T[], item: T, index: number) { } export function toArray(): OperatorFunction { - return reduce(toArrayReducer, []) as OperatorFunction; + return reduce(toArrayReducer, [] as T[]); } diff --git a/src/internal/operators/window.ts b/src/internal/operators/window.ts index 8297df9001..f373dbc399 100644 --- a/src/internal/operators/window.ts +++ b/src/internal/operators/window.ts @@ -24,9 +24,9 @@ import { Operator } from '../Operator'; * * ## Example * In every window of 1 second each, emit at most 2 click events - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; - * import { window, mergeAll, map take } from 'rxjs/operators'; + * import { window, mergeAll, map, take } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); * const sec = interval(1000); diff --git a/src/internal/operators/windowCount.ts b/src/internal/operators/windowCount.ts index fc59fa392b..cccfd3513d 100644 --- a/src/internal/operators/windowCount.ts +++ b/src/internal/operators/windowCount.ts @@ -24,21 +24,21 @@ import { OperatorFunction } from '../types'; * * ## Examples * Ignore every 3rd click event, starting from the first one - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; - * import { windowCount, map, mergeAll } from 'rxjs/operators'; + * import { windowCount, map, mergeAll, skip } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); * const result = clicks.pipe( - * windowCount(3)), - * map(win => win.skip(1)), // skip first of every 3 clicks - * mergeAll(), // flatten the Observable-of-Observables + * windowCount(3), + * map(win => win.pipe(skip(1))), // skip first of every 3 clicks + * mergeAll() // flatten the Observable-of-Observables * ); * result.subscribe(x => console.log(x)); * ``` * * Ignore every 3rd click event, starting from the third one - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { windowCount, mergeAll } from 'rxjs/operators'; * diff --git a/src/internal/operators/windowTime.ts b/src/internal/operators/windowTime.ts index 2877c48cfc..46c1252576 100644 --- a/src/internal/operators/windowTime.ts +++ b/src/internal/operators/windowTime.ts @@ -32,35 +32,35 @@ import { OperatorFunction, SchedulerLike, SchedulerAction } from '../types'; * * ## Examples * In every window of 1 second each, emit at most 2 click events - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; - * import { windowTime, map, mergeAll } from 'rxjs/operators'; + * import { windowTime, map, mergeAll, take } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); * const result = clicks.pipe( * windowTime(1000), - * map(win => win.take(2)), // each window has at most 2 emissions - * mergeAll(), // flatten the Observable-of-Observables + * map(win => win.pipe(take(2))), // each window has at most 2 emissions + * mergeAll(), // flatten the Observable-of-Observables * ); * result.subscribe(x => console.log(x)); * ``` * * Every 5 seconds start a window 1 second long, and emit at most 2 click events per window - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; - * import { windowTime, map, mergeAll } from 'rxjs/operators'; + * import { windowTime, map, mergeAll, take } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); * const result = clicks.pipe( * windowTime(1000, 5000), - * map(win => win.take(2)), // each window has at most 2 emissions - * mergeAll(), // flatten the Observable-of-Observables + * map(win => win.pipe(take(2))), // each window has at most 2 emissions + * mergeAll(), // flatten the Observable-of-Observables * ); * result.subscribe(x => console.log(x)); * ``` * * Same as example above but with maxWindowCount instead of take - * ```javascript + * ```ts * import { fromEvent } from 'rxjs'; * import { windowTime, mergeAll } from 'rxjs/operators'; * diff --git a/src/internal/operators/windowToggle.ts b/src/internal/operators/windowToggle.ts index 48c46a06d8..c2fc2186e1 100644 --- a/src/internal/operators/windowToggle.ts +++ b/src/internal/operators/windowToggle.ts @@ -26,15 +26,15 @@ import { OperatorFunction } from '../types'; * * ## Example * Every other second, emit the click events from the next 500ms - * ```javascript - * import { fromEvent, interval } from 'rxjs'; + * ```ts + * import { fromEvent, interval, EMPTY } from 'rxjs'; * import { windowToggle, mergeAll } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); * const openings = interval(1000); * const result = clicks.pipe( - * windowToggle(openings, i => i % 2 ? interval(500) : empty()), - * mergeAll(), + * windowToggle(openings, i => i % 2 ? interval(500) : EMPTY), + * mergeAll() * ); * result.subscribe(x => console.log(x)); * ``` diff --git a/src/internal/operators/windowWhen.ts b/src/internal/operators/windowWhen.ts index 556c6b258a..aa0cf2cf67 100644 --- a/src/internal/operators/windowWhen.ts +++ b/src/internal/operators/windowWhen.ts @@ -26,15 +26,15 @@ import { OperatorFunction } from '../types'; * * ## Example * Emit only the first two clicks events in every window of [1-5] random seconds - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; - * import { windowWhen, map, mergeAll } from 'rxjs/operators'; + * import { windowWhen, map, mergeAll, take } from 'rxjs/operators'; * * const clicks = fromEvent(document, 'click'); * const result = clicks.pipe( * windowWhen(() => interval(1000 + Math.random() * 4000)), * map(win => win.pipe(take(2))), // each window has at most 2 emissions - * mergeAll(), // flatten the Observable-of-Observables + * mergeAll() // flatten the Observable-of-Observables * ); * result.subscribe(x => console.log(x)); * ``` diff --git a/src/internal/operators/withLatestFrom.ts b/src/internal/operators/withLatestFrom.ts index 5d5b7cab9c..85586b34d1 100644 --- a/src/internal/operators/withLatestFrom.ts +++ b/src/internal/operators/withLatestFrom.ts @@ -43,7 +43,7 @@ export function withLatestFrom(array: ObservableInput[], project: (.. * * ## Example * On every click event, emit an array with the latest timer event plus the click event - * ```javascript + * ```ts * import { fromEvent, interval } from 'rxjs'; * import { withLatestFrom } from 'rxjs/operators'; * diff --git a/src/internal/scheduled/scheduleArray.ts b/src/internal/scheduled/scheduleArray.ts new file mode 100644 index 0000000000..1b5686064e --- /dev/null +++ b/src/internal/scheduled/scheduleArray.ts @@ -0,0 +1,21 @@ +import { Observable } from '../Observable'; +import { SchedulerLike } from '../types'; +import { Subscription } from '../Subscription'; + +export function scheduleArray(input: ArrayLike, scheduler: SchedulerLike) { + return new Observable(subscriber => { + const sub = new Subscription(); + let i = 0; + sub.add(scheduler.schedule(function () { + if (i === input.length) { + subscriber.complete(); + return; + } + subscriber.next(input[i++]); + if (!subscriber.closed) { + sub.add(this.schedule()); + } + })); + return sub; + }); +} diff --git a/src/internal/scheduled/scheduleIterable.ts b/src/internal/scheduled/scheduleIterable.ts new file mode 100644 index 0000000000..dc9043825e --- /dev/null +++ b/src/internal/scheduled/scheduleIterable.ts @@ -0,0 +1,45 @@ +import { Observable } from '../Observable'; +import { SchedulerLike } from '../types'; +import { Subscription } from '../Subscription'; +import { iterator as Symbol_iterator } from '../symbol/iterator'; + +export function scheduleIterable(input: Iterable, scheduler: SchedulerLike) { + if (!input) { + throw new Error('Iterable cannot be null'); + } + return new Observable(subscriber => { + const sub = new Subscription(); + let iterator: Iterator; + sub.add(() => { + // Finalize generators + if (iterator && typeof iterator.return === 'function') { + iterator.return(); + } + }); + sub.add(scheduler.schedule(() => { + iterator = input[Symbol_iterator](); + sub.add(scheduler.schedule(function () { + if (subscriber.closed) { + return; + } + let value: T; + let done: boolean; + try { + const result = iterator.next(); + value = result.value; + done = result.done; + } catch (err) { + subscriber.error(err); + return; + } + if (done) { + subscriber.complete(); + } else { + subscriber.next(value); + this.schedule(); + } + })); + })); + return sub; + }); +} diff --git a/src/internal/scheduled/scheduleObservable.ts b/src/internal/scheduled/scheduleObservable.ts new file mode 100644 index 0000000000..9e970c090d --- /dev/null +++ b/src/internal/scheduled/scheduleObservable.ts @@ -0,0 +1,19 @@ +import { Observable } from '../Observable'; +import { Subscription } from '../Subscription'; +import { observable as Symbol_observable } from '../symbol/observable'; +import { InteropObservable, SchedulerLike, Subscribable } from '../types'; + +export function scheduleObservable(input: InteropObservable, scheduler: SchedulerLike) { + return new Observable(subscriber => { + const sub = new Subscription(); + sub.add(scheduler.schedule(() => { + const observable: Subscribable = input[Symbol_observable](); + sub.add(observable.subscribe({ + next(value) { sub.add(scheduler.schedule(() => subscriber.next(value))); }, + error(err) { sub.add(scheduler.schedule(() => subscriber.error(err))); }, + complete() { sub.add(scheduler.schedule(() => subscriber.complete())); }, + })); + })); + return sub; + }); +} diff --git a/src/internal/scheduled/schedulePromise.ts b/src/internal/scheduled/schedulePromise.ts new file mode 100644 index 0000000000..ec1bfafc94 --- /dev/null +++ b/src/internal/scheduled/schedulePromise.ts @@ -0,0 +1,21 @@ +import { Observable } from '../Observable'; +import { SchedulerLike } from '../types'; +import { Subscription } from '../Subscription'; + +export function schedulePromise(input: PromiseLike, scheduler: SchedulerLike) { + return new Observable(subscriber => { + const sub = new Subscription(); + sub.add(scheduler.schedule(() => input.then( + value => { + sub.add(scheduler.schedule(() => { + subscriber.next(value); + sub.add(scheduler.schedule(() => subscriber.complete())); + })); + }, + err => { + sub.add(scheduler.schedule(() => subscriber.error(err))); + } + ))); + return sub; + }); +} diff --git a/src/internal/scheduled/scheduled.ts b/src/internal/scheduled/scheduled.ts new file mode 100644 index 0000000000..ec819f7301 --- /dev/null +++ b/src/internal/scheduled/scheduled.ts @@ -0,0 +1,36 @@ +import { scheduleObservable } from './scheduleObservable'; +import { schedulePromise } from './schedulePromise'; +import { scheduleArray } from './scheduleArray'; +import { scheduleIterable } from './scheduleIterable'; +import { ObservableInput, SchedulerLike, Observable } from 'rxjs'; +import { isInteropObservable } from '../util/isInteropObservable'; +import { isPromise } from '../util/isPromise'; +import { isArrayLike } from '../util/isArrayLike'; +import { isIterable } from '../util/isIterable'; + +/** + * Converts from a common {@link ObservableInput} type to an observable where subscription and emissions + * are scheduled on the provided scheduler. + * + * @see from + * @see of + * + * @param input The observable, array, promise, iterable, etc you would like to schedule + * @param scheduler The scheduler to use to schedule the subscription and emissions from + * the returned observable. + */ +export function scheduled(input: ObservableInput, scheduler: SchedulerLike): Observable { + if (input != null) { + if (isInteropObservable(input)) { + return scheduleObservable(input, scheduler); + } else if (isPromise(input)) { + return schedulePromise(input, scheduler); + } else if (isArrayLike(input)) { + return scheduleArray(input, scheduler); + } else if (isIterable(input) || typeof input === 'string') { + return scheduleIterable(input, scheduler); + } + } + + throw new TypeError((input !== null && typeof input || input) + ' is not observable'); +} diff --git a/src/internal/scheduler/animationFrame.ts b/src/internal/scheduler/animationFrame.ts index 67a700083c..6f16fd6bc9 100644 --- a/src/internal/scheduler/animationFrame.ts +++ b/src/internal/scheduler/animationFrame.ts @@ -16,7 +16,7 @@ import { AnimationFrameScheduler } from './AnimationFrameScheduler'; * * ## Example * Schedule div height animation - * ```javascript + * ```ts * // html:
* import { animationFrameScheduler } from 'rxjs'; * diff --git a/src/internal/scheduler/asap.ts b/src/internal/scheduler/asap.ts index 68dae7f4b5..93878a17b5 100644 --- a/src/internal/scheduler/asap.ts +++ b/src/internal/scheduler/asap.ts @@ -23,7 +23,7 @@ import { AsapScheduler } from './AsapScheduler'; * * ## Example * Compare async and asap scheduler< - * ```javascript + * ```ts * import { asapScheduler, asyncScheduler } from 'rxjs'; * * asyncScheduler.schedule(() => console.log('async')); // scheduling 'async' first... diff --git a/src/internal/scheduler/async.ts b/src/internal/scheduler/async.ts index ac05aa398e..0a8257ab37 100644 --- a/src/internal/scheduler/async.ts +++ b/src/internal/scheduler/async.ts @@ -17,7 +17,7 @@ import { AsyncScheduler } from './AsyncScheduler'; * * ## Examples * Use async scheduler to delay task - * ```javascript + * ```ts * import { asyncScheduler } from 'rxjs'; * * const task = () => console.log('it works!'); @@ -29,7 +29,7 @@ import { AsyncScheduler } from './AsyncScheduler'; * ``` * * Use async scheduler to repeat task in intervals - * ```javascript + * ```ts * import { asyncScheduler } from 'rxjs'; * * function task(state) { diff --git a/src/internal/scheduler/queue.ts b/src/internal/scheduler/queue.ts index 82f9a4fd4c..f11002fd96 100644 --- a/src/internal/scheduler/queue.ts +++ b/src/internal/scheduler/queue.ts @@ -19,7 +19,7 @@ import { QueueScheduler } from './QueueScheduler'; * * ## Examples * Schedule recursively first, then do something - * ```javascript + * ```ts * import { queueScheduler } from 'rxjs'; * * queueScheduler.schedule(() => { @@ -34,7 +34,7 @@ import { QueueScheduler } from './QueueScheduler'; * ``` * * Reschedule itself recursively - * ```javascript + * ```ts * import { queueScheduler } from 'rxjs'; * * queueScheduler.schedule(function(state) { diff --git a/src/internal/types.ts b/src/internal/types.ts index 9e2d8d0912..e532dc809c 100644 --- a/src/internal/types.ts +++ b/src/internal/types.ts @@ -99,3 +99,5 @@ export interface SchedulerAction extends Subscription { } export type ObservedValueOf = O extends ObservableInput ? T : never; + +export type ObservedValuesFromArray = X extends Array> ? T : never; diff --git a/src/internal/util/hostReportError.ts b/src/internal/util/hostReportError.ts index 87688c074e..6d0082aba4 100644 --- a/src/internal/util/hostReportError.ts +++ b/src/internal/util/hostReportError.ts @@ -4,5 +4,5 @@ * @param err the error to throw */ export function hostReportError(err: any) { - setTimeout(() => { throw err; }); + setTimeout(() => { throw err; }, 0); } \ No newline at end of file diff --git a/src/internal/util/subscribeTo.ts b/src/internal/util/subscribeTo.ts index 27ed81abc1..c872f6a74c 100644 --- a/src/internal/util/subscribeTo.ts +++ b/src/internal/util/subscribeTo.ts @@ -1,4 +1,3 @@ -import { Observable } from '../Observable'; import { ObservableInput } from '../types'; import { subscribeToArray } from './subscribeToArray'; import { subscribeToPromise } from './subscribeToPromise'; @@ -9,20 +8,11 @@ import { isPromise } from './isPromise'; import { isObject } from './isObject'; import { iterator as Symbol_iterator } from '../symbol/iterator'; import { observable as Symbol_observable } from '../symbol/observable'; +import { Subscription } from '../Subscription'; import { Subscriber } from '../Subscriber'; -export const subscribeTo = (result: ObservableInput) => { - if (result instanceof Observable) { - return (subscriber: Subscriber) => { - if (result._isScalar) { - subscriber.next((result as any).value); - subscriber.complete(); - return undefined; - } else { - return result.subscribe(subscriber); - } - }; - } else if (!!result && typeof result[Symbol_observable] === 'function') { +export const subscribeTo = (result: ObservableInput): (subscriber: Subscriber) => Subscription | void => { + if (!!result && typeof result[Symbol_observable] === 'function') { return subscribeToObservable(result as any); } else if (isArrayLike(result)) { return subscribeToArray(result); diff --git a/src/internal/util/subscribeToArray.ts b/src/internal/util/subscribeToArray.ts index 54bbbc9443..0ca529428b 100644 --- a/src/internal/util/subscribeToArray.ts +++ b/src/internal/util/subscribeToArray.ts @@ -8,7 +8,5 @@ export const subscribeToArray = (array: ArrayLike) => (subscriber: Subscri for (let i = 0, len = array.length; i < len && !subscriber.closed; i++) { subscriber.next(array[i]); } - if (!subscriber.closed) { - subscriber.complete(); - } + subscriber.complete(); }; diff --git a/src/internal/util/subscribeToResult.ts b/src/internal/util/subscribeToResult.ts index 4a8df42117..f350b85733 100644 --- a/src/internal/util/subscribeToResult.ts +++ b/src/internal/util/subscribeToResult.ts @@ -1,9 +1,9 @@ -import { ObservableInput } from '../types'; import { Subscription } from '../Subscription'; import { InnerSubscriber } from '../InnerSubscriber'; import { OuterSubscriber } from '../OuterSubscriber'; import { Subscriber } from '../Subscriber'; import { subscribeTo } from './subscribeTo'; +import { Observable } from '../Observable'; export function subscribeToResult( outerSubscriber: OuterSubscriber, @@ -20,7 +20,10 @@ export function subscribeToResult( destination: Subscriber = new InnerSubscriber(outerSubscriber, outerValue, outerIndex) ): Subscription | void { if (destination.closed) { - return; + return undefined; + } + if (result instanceof Observable) { + return result.subscribe(destination); } return subscribeTo(result)(destination); } diff --git a/src/operators/BUILD.bazel b/src/operators/BUILD.bazel deleted file mode 100644 index 1906684307..0000000000 --- a/src/operators/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") - -ts_library( - name = "operators", - srcs = ["index.ts"], - module_name = "rxjs/operators", - module_root = "index.d.ts", - node_modules = "@build_bazel_rules_typescript_tsc_wrapped_deps//:node_modules", - tsconfig = "//:tsconfig.json", - deps = ["//:lib"], -) diff --git a/src/testing/BUILD.bazel b/src/testing/BUILD.bazel deleted file mode 100644 index 004a4007a7..0000000000 --- a/src/testing/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") - -ts_library( - name = "testing", - srcs = ["index.ts"], - module_name = "rxjs/testing", - module_root = "index.d.ts", - node_modules = "@build_bazel_rules_typescript_tsc_wrapped_deps//:node_modules", - tsconfig = "//:tsconfig.json", - deps = ["//:lib"], -) diff --git a/src/webSocket/BUILD.bazel b/src/webSocket/BUILD.bazel deleted file mode 100644 index d95d8be5db..0000000000 --- a/src/webSocket/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") - -ts_library( - name = "webSocket", - srcs = ["index.ts"], - module_name = "rxjs/webSocket", - module_root = "index.d.ts", - node_modules = "@build_bazel_rules_typescript_tsc_wrapped_deps//:node_modules", - tsconfig = "//:tsconfig.json", - deps = ["//:lib"], -) diff --git a/tools/make-umd-compat-bundle.js b/tools/make-umd-compat-bundle.js index 7ba4826303..83e2eb4f3d 100644 --- a/tools/make-umd-compat-bundle.js +++ b/tools/make-umd-compat-bundle.js @@ -8,6 +8,7 @@ rollupBundle({ 'rxjs/operators': 'dist-compat/esm5_for_rollup/src/operators/index.js', 'rxjs/webSocket': 'dist-compat/esm5_for_rollup/src/webSocket/index.js', 'rxjs/ajax': 'dist-compat/esm5_for_rollup/src/ajax/index.js', + 'rxjs/fetch': 'dist-compat/esm5_for_rollup/src/fetch/index.js', 'rxjs/internal-compatibility': 'dist-compat/esm5_for_rollup/src/internal-compatibility/index.js', 'rxjs': 'dist-compat/esm5_for_rollup/src/index.js', }, diff --git a/tsconfig/tsconfig.base.json b/tsconfig/tsconfig.base.json index 7bf2b8ea11..9f53b1292d 100644 --- a/tsconfig/tsconfig.base.json +++ b/tsconfig/tsconfig.base.json @@ -13,6 +13,7 @@ // entry-points "../src/index.ts", "../src/ajax/index.ts", + "../src/fetch/index.ts", "../src/operators/index.ts", "../src/testing/index.ts", "../src/webSocket/index.ts", diff --git a/tsconfig/tsconfig.legacy-reexport.json b/tsconfig/tsconfig.legacy-reexport.json index 3a134b2576..0768fa43de 100644 --- a/tsconfig/tsconfig.legacy-reexport.json +++ b/tsconfig/tsconfig.legacy-reexport.json @@ -16,6 +16,7 @@ "rxjs/internal-compatibility": ["../dist/typings/internal-compatibility/index"], "rxjs/testing": ["../dist/typings/testing/index"], "rxjs/ajax": ["../dist/typings/ajax/index"], + "rxjs/fetch": ["../dist/typings/fetch/index"], "rxjs/operators": ["../dist/typings/operators/index"], "rxjs/webSocket": ["../dist/typings/webSocket/index"], "rxjs-compat": ["../dist-compat/typings/compat/Rx"],