Skip to content

Commit

Permalink
feat(core): improve loop function (#952)
Browse files Browse the repository at this point in the history
  • Loading branch information
aarthificial committed Feb 10, 2024
1 parent 51d8cf0 commit 66c18bb
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 8 deletions.
21 changes: 21 additions & 0 deletions packages/core/src/flow/__logs__/infinite-loop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Make sure to use `yield` or `spawn()` to execute the loop concurrently in a
separate thread:

```ts
// wrong ✗
// prettier-ignore
yield* loop(() => rect().opacity(0).opacity(1, 1));
// correct ✓
yield loop(() => rect().opacity(0).opacity(1, 1));
// correct ✓
spawn(loop(() => rect().opacity(0).opacity(1, 1)));
```

If you want to execute the loop a finite number of times, specify the iteration
count as the first argument:

```ts
// prettier-ignore
yield* loop(10, () => rect().opacity(0).opacity(1, 1));
// ^ iteration count
```
5 changes: 2 additions & 3 deletions packages/core/src/flow/every.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import {decorate, threadable} from '../decorators';
import {ThreadGenerator} from '../threading';
import {usePlayback} from '../utils';

/**
* A callback called by {@link EveryTimer} every N seconds.
*/
export interface EveryCallback {
/**
* A callback called by {@link EveryTimer} every N seconds.
*
* @param tick - The amount of times the timer has ticked.
*/
(tick: number): void;
Expand Down
51 changes: 46 additions & 5 deletions packages/core/src/flow/loop.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
import {decorate, threadable} from '../decorators';
import {ThreadGenerator} from '../threading';
import {useLogger, useThread} from '../utils';
import infiniteLoop from './__logs__/infinite-loop.md';

/**
* A callback called by {@link loop} during each iteration.
*/
export interface LoopCallback {
/**
* A callback called by {@link loop} during each iteration.
*
* @param i - The current iteration index.
*/
(i: number): ThreadGenerator | void;
}

decorate(loop, threadable());

/**
* Run the given generator in a loop.
*
* @remarks
* Each iteration waits until the previous one is completed.
* Because this loop never finishes it cannot be used in the main thread.
* Instead, use `yield` or {@link threading.spawn} to run the loop concurrently.
*
* @example
* Rotate the `rect` indefinitely:
* ```ts
* yield loop(
* () => rect.rotation(0).rotation(360, 2, linear),
* );
* ```
*
* @param factory - A function creating the generator to run. Because generators
* can't be reset, a new generator is created on each
* iteration.
*/
export function loop(factory: LoopCallback): ThreadGenerator;
/**
* Run the given generator N times.
*
Expand All @@ -38,12 +61,30 @@ decorate(loop, threadable());
* can't be reset, a new generator is created on each
* iteration.
*/
export function* loop(
export function loop(
iterations: number,
factory: LoopCallback,
): ThreadGenerator;
export function* loop(
iterations: LoopCallback | number,
factory?: LoopCallback,
): ThreadGenerator {
if (typeof iterations !== 'number') {
factory = iterations;
iterations = Infinity;
}

if (iterations === Infinity && useThread().parent === null) {
useLogger().error({
message: 'Tried to execute an infinite loop in the main thread.',
remarks: infiniteLoop,
stack: new Error().stack,
});
return;
}

for (let i = 0; i < iterations; i++) {
const generator = factory(i);
const generator = factory!(i);
if (generator) {
yield* generator;
} else {
Expand Down

0 comments on commit 66c18bb

Please sign in to comment.