diff --git a/warp-drive-packages/experiments/src/task.ts b/warp-drive-packages/experiments/src/task.ts new file mode 100644 index 00000000000..51ac48e54a8 --- /dev/null +++ b/warp-drive-packages/experiments/src/task.ts @@ -0,0 +1,190 @@ +/** + * Tasks are groups of synchronous and asynchronous operations + * that should be performed as the result of an event such as + * a user interaction. + * + * Tasks have no `this`, they are isolated and should be treated + * as pure functions that operate from a starting state set at + * the moment the task is created to produce a final result. + * + * Tasks can yield values, which can be any value, including + * asynchronous values. + * + * If the value is asynchronous, the task will pause until the + * promise resolves, and then continue execution. + * + * Tasks always run to completion by default, meaning that they will + * not be cancelled or interrupted. + * + * yield always returns both the awaited result, the state of the + * task (e.g. cancelled) and the state of the application instance (e.g. destroyed) + * + * ```ts + * const state = yield Promise.resolve('hello'); + * state.cancelled; // false; + * state.live; // true; + * state.value; // 'hello'; + * ``` + * + * if the task errors: + * - + * + * if a yielded promise rejects: + * - + * + * each yielded value is exposed by: + * - + * + * the return value is: + * - + * + * Recommendations: + * - define tasks in module scope + * - don't cancel tasks + * - don't abort network requests + * - don't debounce + * - if you do debounce, debounce the creation itself + * + * ```ts + * const MyTask = createTask(function* () {}); + * + * class MyComponent extends Component { + * @signal task = null; + * + * doWork = (name) => { + * this.task = myTask(name); + * } + * } + * ``` + * + * // example of a task created at component creation + * // example of a task using a request to update a value + */ + +type DEPTHCOUNT = + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17 + | 18 + | 19 + | 20 + | 21 + | 22 + | 23 + | 24 + | 25 + | 26 + | 27 + | 28 + | 29 + | 30; +// prettier-ignore +type INC_DEPTH = + START extends 0 ? 1 : + START extends 1 ? 2 : + START extends 2 ? 3 : + START extends 3 ? 4 : + START extends 4 ? 5 : + START extends 5 ? 6 : + START extends 6 ? 7 : + START extends 7 ? 8 : + START extends 8 ? 9 : + START extends 9 ? 10 : + START extends 10 ? 11 : + START extends 11 ? 12 : + START extends 12 ? 13 : + START extends 13 ? 14 : + START extends 14 ? 15 : + START extends 15 ? 16 : + START extends 16 ? 17 : + START extends 17 ? 18 : + START extends 18 ? 19 : + START extends 19 ? 20 : + START extends 20 ? 21 : + START extends 21 ? 22 : + START extends 22 ? 23 : + START extends 23 ? 24 : + START extends 24 ? 25 : + START extends 25 ? 26 : + START extends 26 ? 27 : + START extends 27 ? 28 : + START extends 28 ? 29 : + START extends 29 ? 30 : never; + +type SeriesItem = { + order: Index; + input: Awaited; + output: V; + next: Series[INC_DEPTH] extends undefined + ? undefined + : SeriesItem], INC_DEPTH>; +}; + +type Arr = unknown[] | Readonly; + +type Series = T[0] extends undefined ? undefined : SeriesItem; + +const a = [1, 4, 'hello', null, 10, 'hi', { hello: 'world' }] as const; + +type Yield1 = Series; +type Yield2 = Yield1['next']; +type Yield3 = Yield2['next']; +type Yield4 = Yield3['next']; +type Yield5 = Yield4['next']; +type Yield6 = Yield5['next']; +type Yield7 = Yield6['next']; +type Yield8 = Yield7['next']; + +function* doWork() {} + +interface Generator extends IteratorObject { + // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. + next(...[value]: [] | [TNext]): IteratorResult; + return(value: TReturn): IteratorResult; + throw(e: any): IteratorResult; + [Symbol.iterator](): Generator; +} + +interface BetterGeneratorResult { + done?: boolean; + value: TYield; +} + +interface BetterGenerator> { + next(...[value]: Ser extends undefined ? undefined : [Ser['input']]): BetterGeneratorResult; + return(value: TReturn): BetterGeneratorResult; + throw(e: any): BetterGeneratorResult; + [Symbol.iterator](): BetterGenerator; +} + +type Arr = unknown[] | Readonly; + +export type Series = T[0] extends undefined ? undefined : SeriesItem; + +export function createTask(fn: () => Generator) {} + +interface GeneratorTask { + (...args: T): void; +} + +createTask(function* () { + const a = yield Promise.resolve(); + const b = yield 1; + const c = yield { hello: 'world' }; + return null; +});