Mirror: 🎩 A tiny but capable push & pull stream library for TypeScript and Flow

Finalise the documentation (#34)

* Update README

* Add docs for fromObservable

* Add docs for fromCallbag

* Add docs for toCallbag and toObservable

* Add docs for buffer

* Add docs for concatAll

* Add docs for mergeAll and mergeMap

* Add docs for share

* Add docs for switchMap and switchAll

* Fix filename of interval source

* Add docs for interval

* Reformat code snippets in operators doc

* Add docs for debounce and throttle

* Move sample operator out of web

The sample operator doesn't use Web-specific APIs
and can hence be moved to be a generic operator.

* Add docs for sample

* Add notes on interoperability to Getting Started

+20 -53
README.md
···
![Wonka](/docs/wonka.jpg?raw=true)
-
* [What is `Wonka`](#what-is-wonka)
-
* [Why it exists](#why-it-exists)
-
* [Installation](#installation)
-
* [Getting Started](#getting-started)
-
* [Documentation (In Progress)](#documentation)
+
Wonka is a lightweight iterable and observable library loosely based on
+
the [callbag spec](https://github.com/callbag/callbag). It exposes a set of helpers to create streams,
+
which are sources of multiple values, which allow you to create, transform
+
and consume event streams or iterable sets of data.
-
## What is `Wonka`
+
Wonka is written in [Reason](https://reasonml.github.io/), a dialect of OCaml, and can hence be used
+
for native applications. It is also compiled using [BuckleScript](https://bucklescript.github.io) to plain
+
JavaScript and has typings for [TypeScript](https://www.typescriptlang.org/) and [Flow](https://flow.org/).
-
`Wonka` is a library for lightweight observables and iterables loosely based on the [callbag spec](https://github.com/callbag/callbag).
-
It exposes a set of helpers to create and transform sources and output sinks, meaning it helps you to turn an event source or an
-
iterable set of data into streams, and manipulate these streams.
+
This means that out of the box Wonka is usable in any project that use the following:
-
Reason has been becoming increasingly popular, but it's missing a good pattern for streams that feels native to the language.
-
The functional nature of callbags make them a perfect starting point to fix this, and to introduce a reactive programming
-
pattern to a language that is well suited for it.
-
-
This library also attempts to support as many Reason/JS environments as possible, which makes the adoption of streams across
-
multiple projects a lot easier. Hence `Wonka` is a library that aims to make complex streams of data easy to deal with.
-
-
## Compatibility
-
-
`Wonka` is not only compatible with Reason/Bucklescript, but out of the box with other environments as well.
-
+
- Plain JavaScript
- TypeScript
-
- JS/Flow
-
- Reason/OCaml Bucklescript
-
- Reason/OCaml `bs-native`
-
- Reason/OCaml Dune
+
- Flow
+
- Reason/OCaml with BuckleScript
+
- Reason/OCaml with `bs-native`
+
- Reason/OCaml with Dune and Esy
-
In summary, it should work in any TypeScript/Flow/Reason/OCaml environment with full type safety.
-
-
## Installation
-
-
Install the library first: `yarn add wonka` or `npm install --save wonka`,
-
-
### BuckleScript
-
-
For Bucklescript you will also need to add `wonka` to `bs-dependencies` in your `bsconfig.json` file like so:
-
-
```diff
-
{
-
"name": "<your name>",
-
"version": "0.1.0",
-
"sources": ["src"],
-
"bsc-flags": ["-bs-super-errors"],
-
"bs-dependencies": [
-
+ "wonka"
-
]
-
}
-
```
+
## [Documentation](https://wonka.kitten.sh/)
-
## Documentation
+
**See the documentation at [wonka.kitten.sh](https://wonka.kitten.sh)** for more information about using `wonka`!
-
This is still a work-in-progress but will contain full information on the following
-
across all supported languages:
+
- [Introduction](https://wonka.kitten.sh/)
+
- [**Getting started**](https://wonka.kitten.sh/getting-started)
+
- [Basics](https://wonka.kitten.sh/basics/)
+
- [API Reference](https://wonka.kitten.sh/api/)
-
- The API, i.e. a list of all helpers
-
- Examples
-
- Usage Guides & Recipes
-
- Developer Guides (How to write a source/operator/sink)
-
- Modified Callbag spec
+
The raw markdown files can be found [in this repository in the `docs` folder](https://github.com/kitten/wonka/tree/master/docs).
+59 -59
__tests__/wonka_test.re
···
});
});
+
describe("sample", () => {
+
open Expect;
+
open! Expect.Operators;
+
+
afterEach(() => Jest.useRealTimers());
+
+
it("should sample the last emitted value from a source", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.interval(50);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = Wonka.sample(Wonka.interval(100), a);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(200);
+
+
expect(signals) == [|Push(1), Push(3)|];
+
});
+
+
it("should emit an End signal when the source has emitted all values", () => {
+
Jest.useFakeTimers();
+
let a = Wonka.interval(50);
+
+
let talkback = ref((. _: Wonka_types.talkbackT) => ());
+
let signals = [||];
+
+
let source = Wonka.sample(Wonka.interval(100), a) |> Wonka.take(3);
+
+
source((. signal) =>
+
switch (signal) {
+
| Start(x) =>
+
talkback := x;
+
x(. Pull);
+
| Push(_) =>
+
ignore(Js.Array.push(signal, signals));
+
talkback^(. Pull);
+
| End => ignore(Js.Array.push(signal, signals))
+
}
+
);
+
+
Jest.runTimersToTime(300);
+
+
expect(signals) == [|Push(1), Push(3), Push(5), End|];
+
});
+
});
+
describe("scan", () => {
open Expect;
···
Jest.runTimersToTime(3000);
expect(signals) == [|Push(1), Push(2), Push(3), End|];
-
});
-
});
-
-
describe("sample", () => {
-
open Expect;
-
open! Expect.Operators;
-
-
afterEach(() => Jest.useRealTimers());
-
-
it("should sample the last emitted value from a source", () => {
-
Jest.useFakeTimers();
-
let a = Wonka.interval(50);
-
-
let talkback = ref((. _: Wonka_types.talkbackT) => ());
-
let signals = [||];
-
-
let source = WonkaJs.sample(Wonka.interval(100), a);
-
-
source((. signal) =>
-
switch (signal) {
-
| Start(x) =>
-
talkback := x;
-
x(. Pull);
-
| Push(_) =>
-
ignore(Js.Array.push(signal, signals));
-
talkback^(. Pull);
-
| End => ignore(Js.Array.push(signal, signals))
-
}
-
);
-
-
Jest.runTimersToTime(200);
-
-
expect(signals) == [|Push(1), Push(3)|];
-
});
-
-
it("should emit an End signal when the source has emitted all values", () => {
-
Jest.useFakeTimers();
-
let a = Wonka.interval(50);
-
-
let talkback = ref((. _: Wonka_types.talkbackT) => ());
-
let signals = [||];
-
-
let source = WonkaJs.sample(Wonka.interval(100), a) |> Wonka.take(3);
-
-
source((. signal) =>
-
switch (signal) {
-
| Start(x) =>
-
talkback := x;
-
x(. Pull);
-
| Push(_) =>
-
ignore(Js.Array.push(signal, signals));
-
talkback^(. Pull);
-
| End => ignore(Js.Array.push(signal, signals))
-
}
-
);
-
-
Jest.runTimersToTime(300);
-
-
expect(signals) == [|Push(1), Push(3), Push(5), End|];
});
});
+430 -151
docs/api/operators.md
···
Operators in Wonka allow you to transform values from a source before they are sent to a sink. Wonka has the following operators.
+
## buffer
+
+
Buffers emissions from an outer source and emits a buffer array of items every time an
+
inner source (notifier) emits.
+
+
This operator can be used to group values into a arrays on a source. The emitted values will
+
be sent when a notifier fires and will be arrays of all items before the notification event.
+
+
In combination with `interval` this can be used to group values in chunks regularly.
+
+
```reason
+
Wonka.interval(50)
+
|> Wonka.buffer(Wonka.interval(100))
+
|> Wonka.take(2)
+
|> Wonka.subscribe((. buffer) => {
+
Js.Array.forEach(num => print_int(num), buffer);
+
print_endline(";");
+
});
+
/* Prints 1 2; 2 3 to the console. */
+
```
+
+
``` typescript
+
import { pipe, interval, buffer, take, subscribe } from 'wonka';
+
+
pipe(
+
interval(50),
+
buffer(interval(100)),
+
take(2),
+
subscribe(buffer => {
+
buffer.forEach(x => console.log(x));
+
console.log(';');
+
})
+
); // Prints 1 2; 2 3 to the console.
+
```
+
## combine
`combine` two sources together to a single source. The emitted value will be a combination of the two sources, with all values from the first source being emitted with the first value of the second source _before_ values of the second source are emitted.
···
let sourceTwo = Wonka.fromArray([|4, 5, 6|]);
Wonka.combine(sourceOne, sourceTwo)
-
|> Wonka.subscribe((. (_valOne, _valTwo)) => print_int(_valOne + _valTwo));
+
|> Wonka.subscribe((. (a, b)) => print_int(a + b));
/* Prints 56789 (1+4, 2+4, 3+4, 3+5, 3+6) to the console. */
```
···
subscribe(([valOne, valTwo]) => {
console.log(valOne + valTwo);
})
-
);
-
-
// Prints 56789 (1+4, 2+4, 3+4, 3+5, 3+6) to the console.
+
); // Prints 56789 (1+4, 2+4, 3+4, 3+5, 3+6) to the console.
```
## concat
···
`concat` will combine two sources together, subscribing to the next source after the previous source completes.
```reason
-
let sourceOne = Wonka.fromArray([|1, 2, 3, 4, 5, 6|]);
-
let sourceTwo = Wonka.fromArray([|6, 5, 4, 3, 2, 1|]);
-
-
Wonka.concat([|sourceOne, sourceTwo|]) |> Wonka.subscribe((. _val) => print_int(_val));
+
let sourceOne = Wonka.fromArray([|1, 2, 3|]);
+
let sourceTwo = Wonka.fromArray([|6, 5, 4|]);
-
/* Prints 1 2 3 4 5 6 6 5 4 3 2 1 to the console. */
+
Wonka.concat([|sourceOne, sourceTwo|])
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* Prints 1 2 3 6 5 4 to the console. */
```
```typescript
import { fromArray, pipe, concat, subscribe } from 'wonka';
-
const sourceOne = fromArray([1, 2, 3, 4, 5, 6]);
-
const sourceTwo = fromArray([6, 5, 4, 3, 2, 1]);
+
const sourceOne = fromArray([1, 2, 3]);
+
const sourceTwo = fromArray([6, 5, 4]);
pipe(
concat([sourceOne, sourceTwo]),
-
subscribe(val => {
-
console.log(val);
-
})
-
);
+
subscribe(val => console.log(val))
+
); // Prints 1 2 3 6 5 4 to the console.
+
```
-
// Prints 1 2 3 4 5 6 6 5 4 3 2 1 to the console.
+
## concatAll
+
+
`concatAll` will combine all sources emitted on an outer source together, subscribing to the
+
next source after the previous source completes.
+
+
It's very similar to `concat`, but instead accepts a source of sources as an input.
+
+
```reason
+
let sourceOne = Wonka.fromArray([|1, 2, 3|]);
+
let sourceTwo = Wonka.fromArray([|6, 5, 4|]);
+
+
Wonka.fromList([sourceOne, sourceTwo])
+
|> Wonka.concatAll
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* Prints 1 2 3 6 5 4 to the console. */
+
```
+
+
```typescript
+
import { pipe, fromArray, concatAll, subscribe } from 'wonka';
+
+
const sourceOne = fromArray([1, 2, 3]);
+
const sourceTwo = fromArray([6, 5, 4]);
+
+
pipe(
+
fromArray([sourceOne, sourceTwo]),
+
concatAll,
+
subscribe(val => console.log(val))
+
); // Prints 1 2 3 6 5 4 to the console.
```
## concatMap
···
delay(val * 1000)
);
}),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
+
);
+
```
+
+
+
## delay
+
+
`delay` delays all emitted values of a source by the given amount of milliseconds.
+
+
> _Note:_ This operator is only available in JavaScript environments, and will be excluded
+
> when compiling natively.
+
+
```reason
+
Wonka.fromList([1, 2])
+
|> Wonka.delay(10)
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* waits 10ms then prints 1, waits 10ms then prints 2, waits 10ms then ends */
+
```
+
+
```typescript
+
import { pipe, fromArray, delay, subscribe } from 'wonka';
+
+
pipe(
+
fromArray([1, 2]),
+
delay(10)
+
subscribe(val => console.log(val))
+
);
+
// waits 10ms then prints 1, waits 10ms then prints 2, waits 10ms then ends
+
```
+
+
## debounce
+
+
`debounce` doesn't emit values of a source until no values have been emitted after
+
a given amount of milliseconds. Once this threshold of silence has been reached, the
+
last value that has been received will be emitted.
+
+
> _Note:_ This operator is only available in JavaScript environments, and will be excluded
+
> when compiling natively.
+
+
```reason
+
let sourceA = Wonka.interval(10)
+
|> Wonka.take(5);
+
let sourceB = Wonka.fromValue(1);
+
+
Wonka.concat([|sourceA, sourceB|])
+
|> Wonka.debounce((. _x) => 20)
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* The five values from sourceA will be omitted */
+
/* After these values and after 20ms `1` will be logged */
+
```
+
+
```typescript
+
import { pipe, interval, take, fromValue, concat, debounce, subscribe } from 'wonka';
+
+
const sourceA = pipe(interval(10), take(5));
+
const sourceB = fromValue(1);
+
+
pipe(
+
concat([sourceA, sourceB])
+
debounce(() => 20),
+
subscribe(val => console.log(val))
);
+
+
// The five values from sourceA will be omitted
+
// After these values and after 20ms `1` will be logged
```
## filter
···
`filter` will remove values from a source by passing them through an iteratee that returns a `bool`.
```reason
-
let source = Wonka.fromArray([|1, 2, 3, 4, 5, 6|]);
let isEven = (. n) => n mod 2 === 0;
-
source |> Wonka.filter(isEven) |> Wonka.subscribe((. _val) => print_int(_val));
-
+
Wonka.fromArray([|1, 2, 3, 4, 5, 6|])
+
|> Wonka.filter(isEven)
+
|> Wonka.subscribe((. x) => print_int(x));
/* Prints 246 to the console. */
```
```typescript
import { fromArray, filter, subscribe } from 'wonka';
-
const source = fromArray([1, 2, 3, 4, 5, 6]);
const isEven = n => n % 2 === 0;
pipe(
-
source,
+
fromArray([1, 2, 3, 4, 5, 6]),
filter(isEven),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
);
// Prints 246 to the console.
···
`map` will transform values from a source by passing them through an iteratee that returns a new value.
```reason
-
let source = Wonka.fromArray([|1, 2, 3, 4, 5, 6|]);
let square = (. n) => n * n;
-
source |> Wonka.map(square) |> Wonka.subscribe((. _val) => print_int(_val));
-
+
Wonka.fromArray([|1, 2, 3, 4, 5, 6|])
+
|> Wonka.map(square)
+
|> Wonka.subscribe((. x) => print_int(x));
/* Prints 1 4 9 16 25 36 to the console. */
```
```typescript
import { fromArray, pipe, map, subscribe } from 'wonka';
-
const source = fromArray([1, 2, 3, 4, 5, 6]);
const square = n => n * n;
pipe(
-
source,
+
fromArray([1, 2, 3, 4, 5, 6]),
map(square),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
);
// Prints 1 4 9 16 25 36 to the console.
···
## merge
-
`merge` two sources together into a single source.
+
`merge` merges an array of sources together into a single source. It subscribes
+
to all sources that it's passed and emits all their values on the output source.
```reason
let sourceA = Wonka.fromArray([|1, 2, 3|]);
let sourceB = Wonka.fromArray([|4, 5, 6|]);
-
Wonka.merge([|sourceA, sourceB|]) |> Wonka.subscribe((. _val) => print_int(_val));
+
Wonka.merge([|sourceA, sourceB|])
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* Prints 1 2 3 4 5 6 to the console. */
+
```
-
/* Prints 1 2 3 4 5 6 to the console.
+
```typescript
+
import { fromArray, pipe, merge, subscribe } from 'wonka';
+
+
const sourceOne = fromArray([1, 2, 3]);
+
const sourceTwo = fromArray([4, 5, 6]);
+
+
pipe(
+
merge(sourceOne, sourceTwo),
+
subscribe((val) => console.log(val))
+
); // Prints 1 2 3 4 5 6 to the console.
+
```
+
+
## mergeAll
+
+
`mergeAll` will merge all sources emitted on an outer source into a single one.
+
It's very similar to `merge`, but instead accepts a source of sources as an input.
+
+
> _Note:_ This operator is also exported as `flatten` which is just an alias for `mergeAll`
+
+
```reason
+
let sourceA = Wonka.fromArray([|1, 2, 3|]);
+
let sourceB = Wonka.fromArray([|4, 5, 6|]);
+
+
Wonka.fromList([sourceA, sourceB])
+
|> Wonka.mergeAll
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* Prints 1 2 3 4 5 6 to the console. */
```
```typescript
-
import { fromArray, pipe, merge, subscribe } from 'wonka';
+
import { pipe, fromArray, mergeAll, subscribe } from 'wonka';
const sourceOne = fromArray([1, 2, 3]);
const sourceTwo = fromArray([4, 5, 6]);
pipe(
-
merge(sourceOne, sourceTwo)
-
subscribe((val) => {
-
console.log(val);
-
})
-
);
+
fromArray([sourceOne, sourceTwo]),
+
mergeAll,
+
subscribe(val => console.log(val))
+
); // Prints 1 2 3 4 5 6 to the console.
+
```
-
// Prints 1 2 3 4 5 6 to the console.
+
## mergeMap
+
+
`mergeMap` allows you to map values of an outer source to an inner source.
+
This allows you to create nested sources for each emitted value, which will
+
all be merged into a single source, like with `mergeAll`.
+
+
Unlike `concatMap` all inner sources will be subscribed to at the same time
+
and all their values will be emitted on the output source as they come in.
+
+
```reason
+
Wonka.fromList([1, 2])
+
|> Wonka.mergeMap((. value) =>
+
Wonka.fromList([value - 1, value]))
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* Prints 0 1 1 2 to the console. */
+
```
+
+
```typescript
+
import { pipe, fromArray, mergeMap, subscribe } from 'wonka';
+
+
pipe(
+
fromArray([1, 2]),
+
mergeMap(x => fromArray([x - 1, x])),
+
subscribe(val => console.log(val))
+
); // Prints 0 1 1 2 to the console.
```
## onEnd
···
let sourceOne = Wonka.fromPromise(promiseOne);
let sourceTwo = Wonka.fromPromise(promiseTwo);
-
let source = Wonka.concat([|sourceOne, sourceTwo|]);
-
source
-
|> Wonka.onEnd((.) => print_endline("onEnd"))
-
|> Wonka.subscribe((. _val) => print_endline(_val));
+
Wonka.concat([|sourceOne, sourceTwo|])
+
|> Wonka.onEnd((.) => print_endline("onEnd"))
+
|> Wonka.subscribe((. x) => print_endline(x));
/* Logs ResolveOne after one second, then ResolveTwo after an additional second, then onEnd immediately. */
```
···
const sourceOne = fromPromise(promiseOne);
const sourceTwo = fromPromise(promiseTwo);
-
const source = concat([sourceOne, sourceTwo]);
pipe(
-
source,
-
onEnd(() => {
-
console.log('onEnd');
-
}),
-
subscribe(val => {
-
console.log(val);
-
})
+
concat([sourceOne, sourceTwo]),
+
onEnd(() => console.log('onEnd')),
+
subscribe(val => console.log(val))
);
// Logs ResolveOne after one second, then ResolveTwo after an additional second, then onEnd immediately.
···
Run a callback on each `Push` signal sent to the sink by the source.
```reason
-
let source = Wonka.fromArray([|1, 2, 3, 4, 5, 6|]);
-
-
Wonka.source
-
|> Wonka.onPush((. _val) => print_string({j|Push $_val|j}))
-
|> Wonka.subscribe((. _val) => print_int(_val));
-
+
Wonka.fromArray([|1, 2, 3, 4, 5, 6|])
+
|> Wonka.onPush((. x) => print_string({j|Push $x|j}))
+
|> Wonka.subscribe((. x) => print_int(x));
/* Prints Push 1 1 Push 2 2 Push 3 3 Push 4 4 Push 5 5 Push 6 6 to the console. */
```
```typescript
import { fromArray, pipe, onPush, subscribe } from 'wonka';
-
-
const source = fromArray([1, 2, 3, 4, 5, 6]);
pipe(
-
source,
-
onPush(val => {
-
console.log(`Push ${val}`);
-
}),
-
subscribe(val => {
-
console.log(val);
-
})
-
);
-
-
// Prints Push 1 1 Push 2 2 Push 3 3 Push 4 4 Push 5 5 Push 6 6 to the console.
+
fromArray([1, 2, 3, 4, 5, 6]),
+
onPush(val => console.log(`Push ${val}`)),
+
subscribe(val => console.log(val))
+
); // Prints Push 1 1 Push 2 2 Push 3 3 Push 4 4 Push 5 5 Push 6 6 to the console.
```
## onStart
···
Js.Global.setTimeout(() => resolve(. "Resolve"), 1000) |> ignore
);
-
let source = Wonka.fromPromise(promise);
-
-
source
-
|> Wonka.onStart((.) => print_endline("onStart"))
-
|> Wonka.subscribe((. _val) => print_endline(_val));
-
+
Wonka.fromPromise(promise)
+
|> Wonka.onStart((.) => print_endline("onStart"))
+
|> Wonka.subscribe((. _val) => print_endline(_val));
/* Logs onStart to the console, pauses for one second to allow the timeout to finish,
then logs "Resolve" to the console. */
```
···
}, 1000);
});
-
const source = fromPromise(promise);
-
pipe(
-
source,
-
onStart(() => {
-
console.log('onStart');
-
}),
-
subscribe(val => {
-
console.log(val);
-
})
+
fromPromise(promise),
+
onStart(() => console.log('onStart')),
+
subscribe(val => console.log(val))
);
// Logs onStart to the console, pauses for one second to allow the timeout to finish,
// then logs "Resolve" to the console.
```
+
## sample
+
+
`sample` emits the previously emitted value from an outer source every time
+
an inner source (notifier) emits.
+
+
In combination with `interval` it can be used to get values from a noisy source
+
more regularly.
+
+
```reason
+
Wonka.interval(10)
+
|> Wonka.sample(Wonka.interval(100))
+
|> Wonka.take(2)
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* Prints 10 20 to the console. */
+
```
+
+
``` typescript
+
import { pipe, interval, sample, take, subscribe } from 'wonka';
+
+
pipe(
+
interval(10),
+
sample(interval(100)),
+
take(2),
+
subscribe(x => console.log(x))
+
); // Prints 10 20 to the console.
+
```
+
## scan
Accumulate emitted values of a source in a accumulator, similar to JavaScript `reduce`.
```reason
-
let source = Wonka.fromArray([|1, 2, 3, 4, 5, 6|]);
-
-
source
-
|> Wonka.scan((. acc, x) => acc + x, 0)
-
|> Wonka.subscribe((. _val) => print_int(_val));
+
Wonka.fromArray([|1, 2, 3, 4, 5, 6|])
+
|> Wonka.scan((. acc, x) => acc + x, 0)
+
|> Wonka.subscribe((. x) => print_int(x));
/* Prints 1 3 6 10 15 21 to the console. */
```
```typescript
import { fromArray, pipe, scan, subscribe } from 'wonka';
-
const source = fromArray([1, 2, 3, 4, 5, 6]);
-
pipe(
-
source,
+
fromArray([1, 2, 3, 4, 5, 6]),
scan((acc, val) => acc + val),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
);
// Prints 1 3 6 10 15 21 to the console.
```
-
## skip
+
## share
+
+
`share` ensures that all subscriptions to the underlying source are shared.
-
`skip` the specified number of emissions from the source.
+
By default Wonka's sources are lazy. They only instantiate themselves and begin
+
emitting signals when they're being subscribed to, since they're also immutable.
+
This means that when a source is used in multiple places, their underlying subscription
+
is not shared. Instead, the entire chain of sources and operators will be instantiated
+
separately every time.
+
+
The `share` operator prevents this by creating an output source that will reuse a single
+
subscription to the parent source, which will be unsubscribed from when no sinks are
+
listening to it anymore.
+
+
This is especially useful if you introduce side-effects to your sources,
+
for instance with `onStart`.
```reason
-
let source = Wonka.fromArray([|1, 2, 3, 4, 5, 6|]);
+
let source = Wonka.never
+
|> Wonka.onStart((.) => print_endline("start"))
+
|> Wonka.share;
-
source |> Wonka.skip(2) |> Wonka.subscribe((. _val) => print_int(_val));
+
/* Without share this would print "start" twice: */
+
Wonka.publish(source);
+
Wonka.publish(source);
+
```
+
+
```typescript
+
import { pipe, never, onStart, share, publish } from 'wonka';
+
+
const source = pipe(
+
never
+
onStart(() => console.log('start')),
+
share
+
);
+
// Without share this would print "start" twice:
+
publish(source);
+
publish(source);
+
```
+
+
## skip
+
+
`skip` the specified number of emissions from the source.
+
+
```reason
+
Wonka.fromArray([|1, 2, 3, 4, 5, 6|])
+
|> Wonka.skip(2)
+
|> Wonka.subscribe((. x) => print_int(x));
/* Prints 3 4 5 6 to the console, since the first two emissions from the source were skipped.
```
```typescript
import { fromArray, pipe, skip, subscribe } from 'wonka';
-
const source = fromArray([1, 2, 3, 4, 5, 6]);
-
pipe(
-
source,
+
fromArray([1, 2, 3, 4, 5, 6]),
skip(2),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
);
```
···
let source = Wonka.interval(100);
let notifier = Wonka.interval(500);
-
source |> Wonka.skipUntil(notifier) |> Wonka.subscribe((. _val) => print_int(_val));
+
source
+
|> Wonka.skipUntil(notifier)
+
|> Wonka.subscribe((. x) => print_int(x));
/* Skips all values emitted by source (0, 1, 2, 3) until notifier emits at 500ms.
Then logs 4 5 6 7 8 9 10... to the console every 500ms. */
···
pipe(
source,
skipUntil(notifier),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
);
// Skips all values emitted by source (0, 1, 2, 3) until notifier emits at 500ms.
···
```typescript
import { fromArray, pipe, skipWhile, subscribe } from 'wonka';
-
const source = fromArray([1, 2, 3, 4, 5, 6]);
-
pipe(
-
source,
+
fromArray([1, 2, 3, 4, 5, 6]),
skipWhile(val => val < 5),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
);
// Prints 5 6 to the console, as 1 2 3 4 all return true for the predicate function.
```
+
## switchMap
+
+
`switchMap` allows you to map values of an outer source to an inner source.
+
The inner source's values will be emitted on the returned output source. If
+
a new inner source is returned, because the outer source emitted a new value
+
before the previous inner source completed, the inner source is closed and unsubscribed
+
from.
+
+
This is similar to `concatMap` but instead of waiting for the last inner source to complete
+
before emitting values from the next, `switchMap` just cancels the previous inner source.
+
+
```reason
+
Wonka.interval(50)
+
|> Wonka.switchMap((. _value) =>
+
Wonka.interval(40))
+
|> Wonka.take(3)
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* Prints 1 2 3 to the console. */
+
/* The inner interval is cancelled after its first value every time */
+
```
+
+
```typescript
+
import { pipe, interval, switchMap, take, subscribe } from 'wonka';
+
+
pipe(
+
interval(50),
+
// The inner interval is cancelled after its first value every time
+
switchMap(value => interval(40)),
+
take(3),
+
subscribe(x => console.log(x))
+
); // Prints 1 2 3 to the console
+
```
+
+
## switchAll
+
+
`switchAll` will combined sources emitted on an outer source together, subscribing
+
to only one source at a time, and cancelling the previous inner source, when it hasn't
+
ended while the next inner source is created.
+
+
It's very similar to `switchMap`, but instead accepts a source of sources.
+
+
```reason
+
Wonka.interval(50)
+
|> Wonka.map((. _value) =>
+
Wonka.interval(40))
+
|> Wonka.switchAll
+
|> Wonka.take(3)
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* Prints 1 2 3 to the console. */
+
```
+
+
```typescript
+
import { pipe, interval, map, switchAll, take, subscribe } from 'wonka';
+
+
pipe(
+
interval(50),
+
map(() => interval(40)),
+
switchAll,
+
take(3),
+
subscribe(x => console.log(x))
+
); // Prints 1 2 3 to the console
+
```
+
+
These examples are practically identical to the `switchMap` examples, but note
+
that `map` was used instead of using `switchMap` directly. This is because combining
+
`map` with a subsequent `switchAll` is the same as using `switchMap`.
+
## take
`take` only a specified number of emissions from the source before completing. `take` is the opposite of `skip`.
```reason
-
let source = Wonka.fromArray([|1, 2, 3, 4, 5, 6|]);
-
-
source |> Wonka.take(3) |> Wonka.subscribe((. _val) => print_int(_val));
-
+
Wonka.fromArray([|1, 2, 3, 4, 5, 6|])
+
|> Wonka.take(3)
+
|> Wonka.subscribe((. x) => print_int(x));
/* Prints 1 2 3 to the console. */
```
```typescript
import { fromArray, pipe, take, subscribe } from 'wonka';
-
const source = fromArray([1, 2, 3, 4, 5, 6]);
-
pipe(
-
source,
+
fromArray([1, 2, 3, 4, 5, 6]),
take(3),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
);
// Prints 1 2 3 to the console.
···
`takeLast` will take only the last n emissions from the source.
```reason
-
let source = Wonka.fromArray([|1, 2, 3, 4, 5, 6|]);
-
-
source |> Wonka.takeLast(3) |> Wonka.subscribe((. _val) => print_int(_val));
-
+
Wonka.fromArray([|1, 2, 3, 4, 5, 6|]);
+
|> Wonka.takeLast(3)
+
|> Wonka.subscribe((. x) => print_int(x));
/* Prints 4 5 6 to the console. */
```
```typescript
import { fromArray, pipe, takeLast, subscribe } from 'wonka';
-
const source = fromArray([1, 2, 3, 4, 5, 6]);
-
pipe(
-
source,
+
fromArray([1, 2, 3, 4, 5, 6]),
takeLast(3),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
);
// Prints 4 5 6 to the console.
···
let notifier = Wonka.interval(500);
source
-
|> Wonka.takeUntil(notifier)
-
|> Wonka.subscribe((. _val) => print_int(_val));
+
|> Wonka.takeUntil(notifier)
+
|> Wonka.subscribe((. x) => print_int(x));
/* Pauses 100ms, prints 0, pauses 100ms, prints 1, pauses 100ms, prints 2, pauses 100ms,
prints 3, pauses 100, then completes (notifier emits). */
···
pipe(
source,
takeUntil(notifier),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
);
// Pauses 100ms, prints 0, pauses 100ms, prints 1, pauses 100ms, prints 2, pauses 100ms,
···
let source = Wonka.fromArray([|1, 2, 3, 4, 5, 6|]);
source
-
|> Wonka.takeWhile((. _val) => _val < 5)
-
|> Wonka.subscribe((. _val) => print_int(_val));
+
|> Wonka.takeWhile((. x) => x < 5)
+
|> Wonka.subscribe((. x) => print_int(x));
/* Prints 1 2 3 4 to the console. */
```
···
pipe(
source,
takeWhile(val => val < 5),
-
subscribe(val => {
-
console.log(val);
-
})
+
subscribe(val => console.log(val))
);
// Prints 1 2 3 4 to the console.
```
+
+
## throttle
+
+
`throttle` emits values of a source, but after each value it will omit all values for
+
the given amount of milliseconds. It enforces a time of silence after each value it
+
receives and skips values while the silence is still ongoing.
+
+
This is very similar to `debounce` but instead of waiting for leading time before a
+
value it waits for trailing time after a value.
+
+
> _Note:_ This operator is only available in JavaScript environments, and will be excluded
+
> when compiling natively.
+
+
```reason
+
Wonka.interval(10)
+
|> Wonka.throttle((. _x) => 50)
+
|> Wonka.take(2)
+
|> Wonka.subscribe((. x) => print_int(x));
+
/* Outputs 0 6 to the console. */
+
```
+
+
```typescript
+
import { pipe, interval, throttle, take, subscribe } from 'wonka';
+
+
pipe(
+
interval(10),
+
throttle(() => 50)
+
takew(2),
+
subscribe(val => console.log(val))
+
); // Outputs 0 6 to the console.
+
```
+77
docs/api/sinks.md
···
If you have a source that doesn't complete and are looking to resolve on the first
value instead of the last, you may have to apply `take(1)` to your source.
+
+
## toObservable
+
+
`toObservable` returns a [spec-compliant JS Observable](https://github.com/tc39/proposal-observable), which emits the same
+
values as a source.
+
+
As per the specification, the Observable is annotated using `Symbol.observable`.
+
+
> _Note:_ This sink is only available in JavaScript environments, and will be excluded
+
> when compiling natively.
+
+
```reason
+
let observable = Wonka.fromArray([|1, 2, 3|])
+
|> Wonka.toObservable;
+
+
observable##subscribe([@bs] {
+
as _;
+
pub next = value => print_int(value);
+
pub complete = () => ();
+
pub error = _ => ();
+
}); /* Prints 1 2 3 to the console. */
+
```
+
+
```typescript
+
import { pipe, fromArray, toObservable } from 'wonka';
+
+
const observable = pipe(
+
fromArray([1, 2, 3]),
+
toObservable,
+
);
+
+
observable.subscribe({
+
next: value => console.log(value),
+
complete: () => {},
+
error: () => {},
+
}); // Prints 1 2 3 to the console.
+
```
+
+
## toCallbag
+
+
`toCallbag` returns a [spec-compliant JS Callbag](https://github.com/callbag/callbag), which emits the same signals
+
as a Wonka source.
+
+
Since Wonka's sources are very similar to callbags and only diverge from the specification
+
minimally, Callbags map to Wonka's sources very closely and `toCallbag` only creates a thin
+
wrapper which is mostly concerned with converting between the type signatures.
+
+
> _Note:_ This sink is only available in JavaScript environments, and will be excluded
+
> when compiling natively.
+
+
```reason
+
/* This example uses the callbag-iterate package for illustrative purposes */
+
[@bs.module] external callbagIterate:
+
(. ('a => unit)) => (. Wonka.callbagT('a)) => unit = "callbag-iterate";
+
+
let callbag = Wonka.fromArray([|1, 2, 3|])
+
|> Wonka.toCallbag;
+
+
callbagIterate(. value => {
+
print_int(value);
+
})(. callbag); /* Prints 1 2 3 to the console. */
+
```
+
+
```typescript
+
import { pipe, fromArray, toCallbag } from 'wonka';
+
+
// This example uses the callbag-iterate package for illustrative purposes
+
import callbagIterate from 'callbag-iterate';
+
+
const callbag = pipe(
+
fromArray([1, 2, 3]),
+
toCallbag,
+
);
+
+
callbagIterate(value => console.log(value))(callbag);
+
// Prints 1 2 3 to the console.
+
```
+109
docs/api/sources.md
···
); // Prints 1 to the console.
```
+
## fromObservable
+
+
`fromObservable` transforms a [spec-compliant JS Observable](https://github.com/tc39/proposal-observable) into a source.
+
The resulting source will behave exactly the same as the Observable that it was
+
passed, so it will start, end, and push values identically.
+
+
> _Note:_ This source is only available in JavaScript environments, and will be excluded
+
> when compiling natively.
+
+
```typescript
+
import { pipe, fromObservable, subscribe } from 'wonka';
+
+
// This example uses zen-observable for illustrative purposes
+
import Observable from 'zen-observable';
+
+
const observable = Observable.from([1, 2, 3]);
+
+
pipe(
+
fromObservable(observable),
+
subscribe(e => console.log(e))
+
); // Prints 1 2 3 to the console
+
```
+
+
If you're using Reason in a JavaScript environment and you're interested in this
+
operator, you may be using a library to create or get Observables.
+
+
Some libraries don't expose Observables with the same BuckleScript type signature
+
that Wonka uses to type them. So while Wonka's `observableT` type is fairly
+
lenient it may not work for you.
+
+
```reason
+
type observableT('a) = {.
+
[@bs.meth] "subscribe": observerT('a) => subscriptionT
+
};
+
```
+
+
To work around this you can create a function that casts your observable type
+
to Wonka's `observableT`.
+
+
```reason
+
type yourObservableType('a);
+
external asObservable: yourObservableType('a) => Wonka.observableT('a) = "%identity";
+
```
+
+
This snippet would create an `asObservable` function, which can type-cast your
+
Observable type to `Wonka.observableT` and compiles away completely.
+
+
## fromCallbag
+
+
`fromCallbag` transforms a [spec-compliant JS Callbag](https://github.com/callbag/callbag) into a source.
+
+
Since Wonka's sources are very similar to callbags and only diverge from the specification
+
minimally, Callbags map to Wonka's sources very closely and the `fromCallbag` wrapper
+
is very thin and mostly concerned with converting between the type signatures.
+
+
> _Note:_ This source is only available in JavaScript environments, and will be excluded
+
> when compiling natively.
+
+
```reason
+
/* This example uses the callbag-from-iter package for illustrative purposes */
+
[@bs.module] external callbagFromArray:
+
array('a) => Wonka.callbagT('a) = "callbag-from-iter";
+
+
let callbag = callbagFromArray([|1, 2, 3|]);
+
+
Wonka.fromCallbag(callbag)
+
|> Wonka.subscribe((. x) => Js.log(x));
+
/* Prints 1 2 3 to the console. */
+
```
+
+
```typescript
+
import { pipe, fromCallbag, subscribe } from 'wonka';
+
+
// This example uses the callbag-from-iter package for illustrative purposes
+
import callbagFromArray from 'callbag-from-iter';
+
+
const callbag = callbagFromArray([1, 2, 3]);
+
+
pipe(
+
fromCallbag(callbag),
+
subscribe(e => console.log(e))
+
); // Prints 1 2 3 to the console.
+
```
+
+
## interval
+
+
`interval` creates a source that emits values after the given amount of milliseconds.
+
Internally it uses `setInterval` to accomplish this.
+
+
> _Note:_ This source is only available in JavaScript environments, and will be excluded
+
> when compiling natively.
+
+
```reason
+
Wonka.interval(50)
+
|> Wonka.subscribe((. x) => Js.log(x));
+
/* Prints 0 1 2... to the console. */
+
/* The incrementing number is logged every 50ms */
+
```
+
+
```typescript
+
import { pipe, interval, subscribe } from 'wonka';
+
+
pipe(
+
interval(50),
+
subscribe(e => console.log(e))
+
); // Prints 0 1 2... to the console.
+
// The incrementing number is logged every 50ms
+
```
+
## empty
This is a source that doesn't emit any values when subscribed to and
+17
docs/getting-started.md
···
It's worth noting that most callbacks in Wonka need to be explicitly uncurried, since
this will help them compile cleanly to JavaScript.
+
+
## Interoperability
+
+
In JavaScript environments, Wonka comes with several utilities that make it easier
+
to interoperate with JavaScript primitives and other libraries:
+
+
- [`fromPromise`](./api/sources.md#frompromise) & [`toPromise`](./api/sinks.md#topromise) can be used to interoperate with Promises
+
- [`fromObservable`](./api/sources.md#fromobservable) & [`toObservable`](./api/sinks.md#toobservable) can be used to interoperate with spec-compliant Observables
+
- [`fromCallbag`](./api/sources.md#fromcallbag) & [`toCallbag`](./api/sinks.md#tocallbag) can be used to interoperate with spec-compliant Callbags
+
+
Furthermore there are a couple of operators that only work in JavaScript environments
+
since they need timing primitives, like `setTimeout` and `setInterval`:
+
+
- [`delay`](./api/operators.md#delay)
+
- [`debounce`](./api/operators.md#debounce)
+
- [`throttle`](./api/operators.md#throttle)
+
- [`interval`](./api/sources.md#interval)
+1 -2
src/web/wonkaJs.d.ts
···
/* operators */
export * from './wonka_operator_debounce';
export * from './wonka_operator_delay';
-
export * from './wonka_operator_interval';
-
export * from './wonka_operator_sample';
export * from './wonka_operator_throttle';
/* sinks */
export * from './wonka_sink_toPromise';
/* sources */
+
export * from './wonka_source_interval';
export * from './wonka_source_fromDomEvent';
export * from './wonka_source_fromListener';
export * from './wonka_source_fromPromise';
+1 -2
src/web/wonkaJs.re
···
/* operators */
include Wonka_operator_debounce;
include Wonka_operator_delay;
-
include Wonka_operator_interval;
-
include Wonka_operator_sample;
include Wonka_operator_throttle;
/* sinks */
include Wonka_sink_toPromise;
/* sources */
+
include Wonka_source_interval;
include Wonka_source_fromDomEvent;
include Wonka_source_fromListener;
include Wonka_source_fromPromise;
src/web/wonka_operator_interval.d.ts src/web/wonka_source_interval.d.ts
src/web/wonka_operator_interval.re src/web/wonka_source_interval.re
src/web/wonka_operator_interval.rei src/web/wonka_source_interval.rei
src/web/wonka_operator_sample.d.ts src/operators/wonka_operator_sample.d.ts
src/web/wonka_operator_sample.re src/operators/wonka_operator_sample.re
src/web/wonka_operator_sample.rei src/operators/wonka_operator_sample.rei
+1
src/wonka.d.ts
···
export * from './operators/wonka_operator_onEnd';
export * from './operators/wonka_operator_onPush';
export * from './operators/wonka_operator_onStart';
+
export * from './operators/wonka_operator_sample';
export * from './operators/wonka_operator_scan';
export * from './operators/wonka_operator_share';
export * from './operators/wonka_operator_skip';
+1
src/wonka_operators.re
···
include Wonka_operator_onEnd;
include Wonka_operator_onPush;
include Wonka_operator_onStart;
+
include Wonka_operator_sample;
include Wonka_operator_scan;
include Wonka_operator_share;
include Wonka_operator_skip;