From 10c8fa7511d0333503871ab299f922867fb5726b Mon Sep 17 00:00:00 2001 From: Sebastien Guillemot Date: Sun, 29 Dec 2024 21:22:37 +0900 Subject: [PATCH 1/2] Allow yielding a chain of then on Task --- lib/run/task.ts | 76 +++++++++++++++++++++++++++++++++++++++++------- lib/types.ts | 27 +++++++++++++++-- test/run.test.ts | 18 ++++++++++++ 3 files changed, 109 insertions(+), 12 deletions(-) diff --git a/lib/run/task.ts b/lib/run/task.ts index 46d4ff7b4..b58433c53 100644 --- a/lib/run/task.ts +++ b/lib/run/task.ts @@ -6,6 +6,10 @@ import { action } from "../instructions.ts"; import type { FrameResult } from "./types.ts"; import { create } from "./create.ts"; +import { call } from "../call.ts"; + +type ThenFulfilledType = ((value: T) => Then | Promise) | undefined | null; +type CatchType = ((reason: any) => Catch | Promise) | undefined | null; export function createTask( frame: Frame, @@ -32,7 +36,7 @@ export function createTask( return promise; }; - let task = create>("Task", {}, { + let task: Task = create>("Task", {}, { *[Symbol.iterator]() { let frameResult = evaluate | void>(() => frame); if (frameResult) { @@ -44,13 +48,38 @@ export function createTask( } } else { return yield* action(function* (resolve, reject) { - awaitResult(resolve, reject); + getPromise().then(resolve, reject); }); } }, - then: (...args) => getPromise().then(...args), - catch: (...args) => getPromise().catch(...args), - finally: (...args) => getPromise().finally(...args), + then: async (onfulfilled?: ThenFulfilledType, onrejected?: CatchType) => { + type NewResult = Result1 | Result2; + const newPromise = getPromise().then(onfulfilled, onrejected); + const future: Future = create>("Future", {}, { + *[Symbol.iterator]() { + return yield* call(() => newPromise); + }, + then: (...args) => newPromise.then(...args), + catch: (...args) => newPromise.catch(...args), + finally: (...args) => newPromise.finally(...args), + }); + return await future; + }, + catch: async (...args) => { + return await task.then(undefined, ...args); + }, + finally: async (onfinally) => { + const newPromise = getPromise().finally(onfinally); + const future: Future = create>("Future", {}, { + *[Symbol.iterator]() { + return yield* call(() => newPromise); + }, + then: (...args) => newPromise.then(...args), + catch: (...args) => newPromise.catch(...args), + finally: (...args) => newPromise.finally(...args), + }); + return await future; + }, halt() { let haltPromise: Promise; let getHaltPromise = () => { @@ -71,7 +100,7 @@ export function createTask( } }); }; - return create>("Future", {}, { + const future: Future = create>("Future", {}, { *[Symbol.iterator]() { let result = evaluate | void>(() => frame); @@ -86,10 +115,37 @@ export function createTask( }); } }, - then: (...args) => getHaltPromise().then(...args), - catch: (...args) => getHaltPromise().catch(...args), - finally: (...args) => getHaltPromise().finally(...args), + // then: (...args) => getHaltPromise().then(...args), + then: async (onfulfilled?: ThenFulfilledType, onrejected?: CatchType) => { + type NewResult = Result1 | Result2; + const newPromise = getHaltPromise().then(onfulfilled, onrejected); + const future: Future = create>("Future", {}, { + *[Symbol.iterator]() { + return yield* call(() => newPromise); + }, + then: (...args) => newPromise.then(...args), + catch: (...args) => newPromise.catch(...args), + finally: (...args) => newPromise.finally(...args), + }); + return await future; + }, + catch: async (...args) => { + return await task.then(undefined, ...args); + }, + finally: async (onfinally) => { + const newPromise = getHaltPromise().finally(onfinally); + const future: Future = create>("Future", {}, { + *[Symbol.iterator]() { + return yield* call(() => newPromise); + }, + then: (...args) => newPromise.then(...args), + catch: (...args) => newPromise.catch(...args), + finally: (...args) => newPromise.finally(...args), + }); + return await future; + }, }); + return future; }, }); return task; @@ -105,4 +161,4 @@ function getResult(result: FrameResult): Result { } else { return result.exit.result; } -} +} \ No newline at end of file diff --git a/lib/types.ts b/lib/types.ts index 4d1ef1072..7dd8a1efa 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -53,7 +53,30 @@ export interface Operation { * things, if the operation resolves synchronously, it will continue within the * same tick of the run loop. */ -export interface Future extends Promise, Operation {} +export interface Future extends Promise, Operation { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Future; + + /** + * Attaches a callback for only the rejection of the Promise. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of the callback. + */ + catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Future; + + /** + * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The + * resolved value cannot be modified from the callback. + * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). + * @returns A Promise for the completion of the callback. + */ + finally(onfinally?: (() => void) | undefined | null): Future; +} /** * A handle to a concurrently running operation that lets you either use the @@ -139,7 +162,7 @@ export interface Task extends Future { * children. * * Any errors raised by the `halt()` operation only represent problems that - * occured during the teardown of the task. In other words, `halt()` can + * occurred during the teardown of the task. In other words, `halt()` can * succeed even if the task failed. * * @returns a future that only resolves when all shutdown associated with this diff --git a/test/run.test.ts b/test/run.test.ts index c764d389b..fb49c9ab7 100644 --- a/test/run.test.ts +++ b/test/run.test.ts @@ -27,6 +27,24 @@ describe("run()", () => { expect(result).toEqual(67); }); + it("can await a task 'then'", () => { + let result = run(function* () { + return yield* Promise.resolve(12); + }); + const plusOne = result.then((value) => value + 1); + expect(plusOne).resolves.toEqual(13); + }); + + it("can yield a task 'then'", async () => { + let result = run(function* () { + return yield* Promise.resolve(12); + }); + const plusOne = result.then((value) => value + 1); + await expect(run(function* () { + return yield* plusOne; + })).resolves.toEqual(13); + }); + it("rejects generator if subtask promise fails", async () => { let error = new Error("boom"); let task = run(function* () { From 6825d4e61f7db3a146cef01c09563ac69bc262da Mon Sep 17 00:00:00 2001 From: Sebastien Guillemot Date: Sun, 29 Dec 2024 21:32:20 +0900 Subject: [PATCH 2/2] remove comment --- lib/run/task.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/run/task.ts b/lib/run/task.ts index b58433c53..0e00fc25d 100644 --- a/lib/run/task.ts +++ b/lib/run/task.ts @@ -115,7 +115,6 @@ export function createTask( }); } }, - // then: (...args) => getHaltPromise().then(...args), then: async (onfulfilled?: ThenFulfilledType, onrejected?: CatchType) => { type NewResult = Result1 | Result2; const newPromise = getHaltPromise().then(onfulfilled, onrejected);