Skip to content

Commit

Permalink
feat(delay): Allow passing array of targets to delay
Browse files Browse the repository at this point in the history
  • Loading branch information
kireevmp committed Jul 5, 2023
1 parent fcdc737 commit a089fc9
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 19 deletions.
46 changes: 46 additions & 0 deletions src/delay/delay.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,49 @@ test('double delay effect', async () => {
]
`);
});

test('delay with array of units', async () => {
expect.assertions(2);

const source = createEvent();

const fnA = jest.fn();
const targetA = createEvent();

targetA.watch(fnA);

const fnB = jest.fn();
const targetB = createEvent();

targetB.watch(fnB);

delay({ source, timeout: 100, target: [targetA, targetB] });

source(1);

await waitFor(targetA);

expect(fnA).toHaveBeenCalledTimes(1);
expect(fnB).toHaveBeenCalledTimes(1);
});

test('delay throws when any of targets is not a unit', async () => {
expect.assertions(1);

const source = createEvent();
const target = createEvent();

expect(() =>
delay({ source, timeout: 100, target: [target, 'not a unit'] }),
).toThrowError(/target must be a unit/);
});

test('delay throws when source is not a unit', async () => {
expect.assertions(1);

const target = createEvent();

expect(() => delay({ source: 'not a unit', timeout: 100, target })).toThrowError(
/source must be a unit/,
);
});
25 changes: 13 additions & 12 deletions src/delay/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import {
createEffect,
createEvent,
forward,
is,
sample,
Unit,
Event,
Store,
Effect,
EventAsReturnType,
combine,
} from 'effector';

type EventAsReturnType<Payload> = any extends Payload ? Event<Payload> : never;
type TargetType<T> =
| Store<T>
| Event<T>
| Effect<T, any, any>
| Event<void>
| Effect<void, any, any>;

export function delay<T>({
source,
Expand All @@ -20,16 +25,13 @@ export function delay<T>({
}: {
source: Unit<T>;
timeout: ((_payload: T) => number) | Store<number> | number;
target?:
| Store<T>
| Event<T>
| Effect<T, any, any>
| Event<void>
| Effect<void, any, any>;
target?: TargetType<T> | [TargetType<T>, ...TargetType<T>[]];
}): EventAsReturnType<T> {
if (!is.unit(source)) throw new TypeError('source must be a unit from effector');
const targets = Array.isArray(target) ? target : [target];

if (!is.unit(target)) throw new TypeError('target must be a unit from effector');
if (!is.unit(source)) throw new TypeError('source must be a unit from effector');
if (!targets.every((unit) => is.unit(unit)))
throw new TypeError('target must be a unit from effector');

const ms = validateTimeout(timeout);

Expand All @@ -53,8 +55,7 @@ export function delay<T>({
target: timerFx,
});

// @ts-expect-error
forward({ from: timerFx.doneData, to: target });
sample({ clock: timerFx.doneData, target: targets });

return timerFx.doneData;
}
Expand Down
22 changes: 15 additions & 7 deletions src/delay/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { delay } from 'patronum/delay';
### Formulae

```ts
target = delay({ source, timeout: number, target });
event = delay({ source, timeout: number, target });
```

- When `source` is triggered, wait for `timeout`, then trigger `target` with payload of the `source`
Expand All @@ -20,11 +20,11 @@ target = delay({ source, timeout: number, target });

1. `source` `(Event<T>` | `Store<T>` | `Effect<T>)` — Source unit, data from this unit used to trigger `target` with.
1. `timeout` `(number)` — time to wait before trigger `event`
1. `target` `(Event<T>` | `Store<T>` | `Effect<T`>)` — Optional. Target unit, that should be called after delay.
1. `target` `(Unit<T>` | `Array<Unit<T>>)` — Optional. Target unit or array of units that will be called after delay.

### Returns

- `event` `(Event<T>)` — New event, that triggered after delay
- `event` `(Event<T>)` — New event that is triggered after delay.

### Example

Expand Down Expand Up @@ -52,7 +52,7 @@ It is useful when you know that calculations requires more time if you have more
### Formulae

```ts
target = delay({ source, timeout: Function, target });
event = delay({ source, timeout: Function, target });
```

- When `source` is triggered, call `timeout` with payload to get the timeout for delay, then trigger `target` with payload of the `source`
Expand All @@ -61,7 +61,11 @@ target = delay({ source, timeout: Function, target });

1. `source` `(Event<T>` | `Store<T>` | `Effect<T>)` — Source unit, data from this unit used to trigger `target` with.
1. `timeout` `((payload: T) => number)` — Calculate delay for each `source` call. Receives the payload of `source` as argument. Should return `number` — delay in milliseconds.
1. `target` `(Event<T>` | `Store<T>` | `Effect<T`>)` — Optional. Target unit, that should be called after delay.
1. `target` `(Unit<T>` | `Array<Unit<T>>)` — Optional. Target unit or array of units that will be called after delay.

### Returns

- `event` `(Event<T>)` — New event that is triggered after delay.

### Example

Expand Down Expand Up @@ -100,7 +104,7 @@ It is useful when you writing music editor and need dynamic delay for your event
### Formulae

```ts
target = delay({ source, timeout: $store, target });
event = delay({ source, timeout: $store, target });
```

- When `source` is triggered, read timeout from `timeout` store, then trigger `target` with payload of the `source`
Expand All @@ -109,7 +113,11 @@ target = delay({ source, timeout: $store, target });

1. `source` `(Event<T>` | `Store<T>` | `Effect<T>)` — Source unit, data from this unit used to trigger `target` with.
1. `timeout` `(Store<number>)` — Store with number — delay in milliseconds.
1. `target` `(Event<T>` | `Store<T>` | `Effect<T`>)` — Optional. Target unit, that should be called after delay.
1. `target` `(Unit<T>` | `Array<Unit<T>>)` — Optional. Target unit or array of units that will be called after delay.

### Returns

- `event` `(Event<T>)` — New event that is triggered after delay.

### Example

Expand Down
43 changes: 43 additions & 0 deletions test-typings/delay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,46 @@ import { delay } from '../src/delay';

expectType<Event<number>>(delay({ source, timeout: 100, target }));
}

// supports multiple targets as an array
{
const source = createStore('');

const $targetStore = createStore('');

const targetEvent = createEvent<string>();
const targetEventVoid = createEvent<void>();

const targetEffect = createEffect<string, void>();
const targetEffectVoid = createEffect<void, void>();

expectType<Event<string>>(
delay({
source,
timeout: 100,
target: [
$targetStore,
targetEvent,
targetEventVoid,
targetEffect,
targetEffectVoid,
],
}),
);
}

// does not allow invalid targets in array
{
const source = createStore('');

// @ts-expect-error
delay({ source, timeout: 100, target: [] });
// @ts-expect-error
delay({ source, timeout: 100, target: ['non-unit'] });
// @ts-expect-error
delay({ source, timeout: 100, target: [null] });
// @ts-expect-error
delay({ source, timeout: 100, target: [100] });
// @ts-expect-error
delay({ source, timeout: 100, target: [() => ''] });
}

0 comments on commit a089fc9

Please sign in to comment.