Skip to content

Commit

Permalink
Add task context
Browse files Browse the repository at this point in the history
  • Loading branch information
Exelord committed Jan 18, 2023
1 parent 89c9235 commit 8308871
Showing 1 changed file with 21 additions and 22 deletions.
43 changes: 21 additions & 22 deletions src/task.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createObject } from "solid-proxies";
import { signalledPromise } from "./utils/promise";

// export type TaskFunction<T, Args extends any[]> = (...args: Args) => Promise<T>;

Expand All @@ -11,6 +12,10 @@ export enum TaskStatus {
Dropped = "dropped",
}

export interface TaskContext {
signal: AbortSignal;
}

export class TaskError extends Error {
name = "TaskError";
}
Expand Down Expand Up @@ -69,7 +74,7 @@ export class Task<T> implements Promise<T> {
}

#promise?: Promise<T>;
#promiseFn: (signal: AbortSignal) => Promise<T>;
#promiseFn: (context: TaskContext) => Promise<T>;
#abortController = new AbortController();

#reactiveState = createObject<{
Expand All @@ -81,7 +86,7 @@ export class Task<T> implements Promise<T> {
status: TaskStatus.Idle,
});

constructor(promiseFn: (signal: AbortSignal) => Promise<T>) {
constructor(promiseFn: (context: TaskContext) => Promise<T>) {
this.#promiseFn = promiseFn;
}

Expand Down Expand Up @@ -141,27 +146,19 @@ export class Task<T> implements Promise<T> {
// We need to check if the task has been cancelled before we start the promise.
this.#abortController.signal.throwIfAborted();

const promise = this.#promiseFn(this.#abortController.signal);
const promise = this.#promiseFn({
signal: this.#abortController.signal,
}).catch((error) => {
if (this.isPending) {
this.#reactiveState.error = error;
this.#reactiveState.status = TaskStatus.Rejected;
}
throw error;
});

this.#reactiveState.status = TaskStatus.Pending;

// We race to make sure we don't wait for the promise to resolve if it has been cancelled.
const value = await Promise.race([
promise.catch((error) => {
if (this.isPending) {
this.#reactiveState.error = error;
this.#reactiveState.status = TaskStatus.Rejected;
}
throw error;
}),

// We need to create a promise that we can reject when the task is cancelled.
new Promise<never>((_resolve, reject) => {
// We listen for the abort event so we can reject the promise.
this.#abortController.signal.addEventListener("abort", () => {
reject(this.#abortController.signal.reason);
});
}),
]);
const value = await signalledPromise(this.#abortController.signal, promise);

this.#reactiveState.value = value;
this.#reactiveState.status = TaskStatus.Fulfilled;
Expand All @@ -170,6 +167,8 @@ export class Task<T> implements Promise<T> {
}
}

export function createTask<T>(promiseFn: () => Promise<T>): Task<T> {
export function createTask<T>(
promiseFn: (context: TaskContext) => Promise<T>
): Task<T> {
return new Task(promiseFn);
}

0 comments on commit 8308871

Please sign in to comment.