diff --git a/.changeset/ninety-trains-smash.md b/.changeset/ninety-trains-smash.md new file mode 100644 index 0000000..9a8eb60 --- /dev/null +++ b/.changeset/ninety-trains-smash.md @@ -0,0 +1,5 @@ +--- +"@effect-rx/rx": patch +--- + +add Rx.debounce diff --git a/docs/rx/Rx.ts.md b/docs/rx/Rx.ts.md index 45872a4..668138c 100644 --- a/docs/rx/Rx.ts.md +++ b/docs/rx/Rx.ts.md @@ -15,12 +15,14 @@ Added in v1.0.0 - [batching](#batching) - [batch](#batch) - [combinators](#combinators) + - [debounce](#debounce) - [initialValue](#initialvalue) - [keepAlive](#keepalive) - [map](#map) - [mapResult](#mapresult) - [refreshable](#refreshable) - [setIdleTTL](#setidlettl) + - [transform](#transform) - [withFallback](#withfallback) - [withLabel](#withlabel) - [constructors](#constructors) @@ -94,6 +96,19 @@ Added in v1.0.0 # combinators +## debounce + +**Signature** + +```ts +export declare const debounce: { + (duration: Duration.DurationInput): >(self: A) => A + >(self: A, duration: Duration.DurationInput): A +} +``` + +Added in v1.0.0 + ## initialValue **Signature** @@ -122,13 +137,15 @@ Added in v1.0.0 **Signature** ```ts -export declare const map: (, B>( - f: (_: Rx.Infer) => B -) => (self: R) => [R] extends [Writable] ? Writable : Rx) & - (, B>( +export declare const map: { + , B>( + f: (_: Rx.Infer) => B + ): (self: R) => [R] extends [Writable] ? Writable : Rx + , B>( self: R, f: (_: Rx.Infer) => B - ) => [R] extends [Writable] ? Writable : Rx) + ): [R] extends [Writable] ? Writable : Rx +} ``` Added in v1.0.0 @@ -180,6 +197,24 @@ export declare const setIdleTTL: { Added in v1.0.0 +## transform + +**Signature** + +```ts +export declare const transform: { + , B>( + f: (get: Context) => B + ): (self: R) => [R] extends [Writable] ? Writable : Rx + , B>( + self: R, + f: (get: Context) => B + ): [R] extends [Writable] ? Writable : Rx +} +``` + +Added in v1.0.0 + ## withFallback **Signature** diff --git a/packages/rx/src/Rx.ts b/packages/rx/src/Rx.ts index 32de5ee..08a149d 100644 --- a/packages/rx/src/Rx.ts +++ b/packages/rx/src/Rx.ts @@ -1,7 +1,6 @@ /** * @since 1.0.0 */ -import type { Context } from "effect" import { NoSuchElementException } from "effect/Cause" import * as Chunk from "effect/Chunk" import * as Duration from "effect/Duration" @@ -1143,33 +1142,52 @@ export const initialValue: { * @since 1.0.0 * @category combinators */ -export const map = dual< +export const transform: { , B>( - f: (_: Rx.Infer) => B - ) => (self: R) => [R] extends [Writable] ? Writable : Rx, + f: (get: Context) => B + ): (self: R) => [R] extends [Writable] ? Writable : Rx , B>( self: R, - f: (_: Rx.Infer) => B - ) => [R] extends [Writable] ? Writable : Rx ->( + f: (get: Context) => B + ): [R] extends [Writable] ? Writable : Rx +} = dual( 2, - ((self: Rx, f: (_: A) => B): Rx => + ((self: Rx, f: (get: Context) => B): Rx => isWritable(self) ? writable( - (get) => f(get(self)), - self.write as any, + f, + function(ctx, value) { + ctx.set(self, value) + }, self.refresh ?? function(refresh) { refresh(self) } ) : readable( - (get) => f(get(self)), + f, self.refresh ?? function(refresh) { refresh(self) } )) as any ) +/** + * @since 1.0.0 + * @category combinators + */ +export const map: { + , B>( + f: (_: Rx.Infer) => B + ): (self: R) => [R] extends [Writable] ? Writable : Rx + , B>( + self: R, + f: (_: Rx.Infer) => B + ): [R] extends [Writable] ? Writable : Rx +} = dual( + 2, + (self: Rx, f: (_: A) => B): Rx => transform(self, (get) => f(get(self))) +) + /** * @since 1.0.0 * @category combinators @@ -1192,6 +1210,38 @@ export const mapResult: { ): [R] extends [Writable] ? Writable>>, RW> : Rx>>> => map(self, Result.map(f))) +/** + * @since 1.0.0 + * @category combinators + */ +export const debounce: { + (duration: Duration.DurationInput): >(self: A) => A + >(self: A, duration: Duration.DurationInput): A +} = dual( + 2, + (self: Rx, duration: Duration.DurationInput): Rx => { + const millis = Duration.toMillis(duration) + return transform(self, function(get) { + let timeout: number | undefined + let value = get.once(self) + function update() { + get.setSelfSync(get.once(self)) + } + get.addFinalizer(function() { + if (timeout !== undefined) { + clearTimeout(timeout) + } + }) + get.subscribe(self, function(val) { + value = val + if (timeout) return + setTimeout(update, millis) + }) + return value + }) + } +) + /** * @since 1.0.0 * @category batching