diff --git a/flow-typed/index.js b/flow-typed/index.js index e43182c..22397b0 100644 --- a/flow-typed/index.js +++ b/flow-typed/index.js @@ -641,6 +641,18 @@ declare export class Task { * _`resolver.Err => Promise>`_ */ fork(resolver: TaskResolver): Promise>; + /** + * `run` begins execution of the Task and returns a Promise resolving with a + * `Result` that contains the success or error value of the Task. + */ + run(): Promise>; + /** + * `run_sync` executes the Task synchronously and returns a `Result` that + * contains the success or error value of the Task. + * + * _NOTE: throws an Error if a callback is not invoked synchronously._ + */ + run_sync(): Result; /** * `map` returns a new Task with the success value mapped according to the * map function given. `map` should be a synchronous operation. diff --git a/src/__snapshots__/task.test.ts.snap b/src/__snapshots__/task.test.ts.snap index ebfd7c6..181b1ba 100644 --- a/src/__snapshots__/task.test.ts.snap +++ b/src/__snapshots__/task.test.ts.snap @@ -1,3 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Task should toString 1`] = `"[object Task]"`; + +exports[`Task task.run_sync (no sync callback Error) 1`] = `"Task.run_sync expects the executor to resolve synchronously."`; diff --git a/src/task.test.ts b/src/task.test.ts index ab89c53..697557e 100644 --- a/src/task.test.ts +++ b/src/task.test.ts @@ -42,6 +42,47 @@ describe("Task", async () => { expect(r).toEqual(Result.Err(value)); }); + it("task.run (success)", async () => { + const final = "success"; + expect(await Task.from(r => r.Ok(final)).run()).toEqual( + Result.Ok(final) + ); + }); + + it("task.run (error)", async () => { + const final = "error"; + expect(await Task.from(r => r.Err(final)).run()).toEqual( + Result.Err(final) + ); + }); + + it("task.run_sync (success)", async () => { + const final = "success"; + expect(Task.from(r => r.Ok(final)).run_sync()).toEqual( + Result.Ok(final) + ); + }); + + it("task.run_sync (error)", async () => { + const final = "error"; + expect(Task.from(r => r.Err(final)).run_sync()).toEqual( + Result.Err(final) + ); + }); + + it("task.run_sync (no sync callback Error)", async () => { + let task_bomb = Task.from(async r => { + await new Promise(r => setTimeout(r, 10)); + r.Ok("#boom"); + }); + + const fn = () => { + task_bomb.run_sync(); + }; + + expect(fn).toThrowErrorMatchingSnapshot(); + }); + it("Task.map", async () => { const a = "a test"; let t = Task.from(r => r.Ok(a)).map(s => s.toUpperCase()); diff --git a/src/task.ts b/src/task.ts index 7e00583..ec73ffe 100644 --- a/src/task.ts +++ b/src/task.ts @@ -1,4 +1,4 @@ -import { Mapper } from "./utils"; +import { Mapper, identity } from "./utils"; import { Result } from "./result"; /** @@ -54,6 +54,48 @@ export class Task { ); } + /** + * `run` begins execution of the Task and returns a Promise resolving with a + * `Result` that contains the success or error value of the Task. + */ + public run(): Promise> { + return this.fork({ + Err: identity, + Ok: identity, + }); + } + + /** + * `run_sync` executes the Task synchronously and returns a `Result` that + * contains the success or error value of the Task. + * + * _NOTE: throws an Error if a callback is not invoked synchronously._ + */ + public run_sync(): Result { + let r: Result; + + // The executor is not guaranteed to return anything. + // We need to use the callbacks to assign our Result. + this.executor({ + Ok(value) { + r = Result.Ok(value); + }, + Err(err) { + r = Result.Err(err); + }, + }); + + // The first bang `!` is for logical not, the second for definite assignment + if (!r!) { + throw new Error( + `Task.run_sync expects the executor to resolve synchronously.` + ); + } + + // We've asserted that `r` is definitely assigned + return r!; + } + /** * `map` returns a new Task with the success value mapped according to the * map function given. `map` should be a synchronous operation.