UNPKG

@daiso-tech/core

Version:

The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.

212 lines 7.33 kB
/** * @module Task */ import { callInvokable, isPromiseLike, resolveAsyncLazyable, } from "../utilities/_module-exports.js"; import { abortAndFail } from "../task/abort-and-fail.js"; import { TimeSpan } from "../time-span/implementations/_module-exports.js"; import { AsyncHooks } from "../hooks/_module-exports.js"; /** * The `Task` class is used for creating lazy {@link PromiseLike | `PromiseLike`} object that will only execute when awaited or when `then` method is called. * Note the class is immutable. * * IMPORT_PATH: `"@daiso-tech/core/task"` */ export class Task { /** * The `wrapFn` is convience method used for wrapping async {@link Invokable | `Invokable`} with a `Task`. * @example * ```ts * import { Task, retry } from "@daiso-tech/core/task"; * import { TimeSpan } from "@daiso-tech/core/time-span" from "@daiso-tech/core/time-span"; * import { readFile as readFileNodeJs } from "node:fs/promises"; * * const readFile = Task.wrapFn(readFileNodeJs); * * const file = await readFile("none_existing_file.txt"); * ``` */ static wrapFn(fn) { return (...parameters) => new Task(() => callInvokable(fn, ...parameters)); } /** * The `delay` method creates a {@link Task | `Task`} that will be fulfilled after given `time`. * * @example * ```ts * import { Task } from "@daiso-tech/core/task"; * import { TimeSpan } from "@daiso-tech/core/time-span" from "@daiso-tech/core/time-span"; * * console.log("a"); * await Task.delay(TimeSpan.fromSeconds(2)); * console.log("b"); * ``` */ static delay(time, abortSignal = new AbortController().signal) { return new Task(async () => { let timeoutId = null; try { await abortAndFail(new Promise((resolve) => { timeoutId = setTimeout(() => { resolve(); }, TimeSpan.fromTimeSpan(time).toMilliseconds()); }), abortSignal); } finally { if (timeoutId !== null) { clearTimeout(timeoutId); } } }); } /** * The `resolve` method works similarly to {@link Promise.resolve | `Promise.resolve`} with the key distinction that it operates lazily. */ static resolve(value) { return new Task(async () => { if (value === undefined) { return; } return await value; }); } /** * The `reject` method works similarly to {@link Promise.reject | `Promise.reject`} with the key distinction that it operates lazily. */ static reject(reason) { return new Task(async () => { // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors return Promise.reject(reason); }); } static toTasks(promises) { const tasks = []; for (const promise of promises) { if (promise instanceof Task) { tasks.push(promise); } if (!isPromiseLike(promise)) { tasks.push(Task.resolve(promise)); } throw new TypeError("!!__MESSAGE__!!"); } return tasks; } /** * The `all` method works similarly to {@link Promise.all | `Promise.all`} with the key distinction that it operates lazily. */ static all(tasks) { return new Task(async () => Promise.all(Task.toTasks(tasks))); } /** * The `allSettled` method works similarly to {@link Promise.allSettled | `Promise.allSettled`} with the key distinction that it operates lazily. */ static allSettled(tasks) { return new Task(async () => Promise.allSettled(Task.toTasks(tasks))); } /** * The `race` method works similarly to {@link Promise.race | `Promise.race`} with the key distinction that it operates lazily. */ static race(tasks) { return new Task(async () => Promise.race(Task.toTasks(tasks))); } /** * The `any` method works similarly to {@link Promise.any | `Promise.any`} with the key distinction that it operates lazily. */ static any(tasks) { return new Task(async () => Promise.any(Task.toTasks(tasks))); } /** * The `fromCallback` is convience method used for wrapping Node js callback functions with a `Task`. * @example * ```ts * import { Task } from "@daiso-tech/core/task"; * import { readFile } from "node:fs"; * * const task = Task.fromCallback<Buffer | string>((resolve, reject) => { * readFile("FILE_PATH", (err, data) => { * if (err !== null) { * reject(err); * return; * } * resolve(data); * }); * }); * const file = await task; * console.log(file); * ``` */ static fromCallback(callback) { return new Task(() => new Promise((resolve, reject) => { callback(resolve, reject); })); } promise = null; invokable; /** * @example * ```ts * import { Task, retry } from "@daiso-tech/core/task"; * * const promise = new Task(async () => { * console.log("I am lazy"); * }, * // You can also pass in one AsyncMiddleware or multiple (as an Array). * retry() * ); * * // "I am lazy" will only logged when awaited or then method i called. * await promise; * ``` * * You can pass sync or async {@link Invokable | `Invokable`}. */ constructor(invokable, middlewares = []) { this.invokable = new AsyncHooks(async () => { return await resolveAsyncLazyable(invokable); }, middlewares); } /** * The `pipe` method returns a new `Task` instance with the additional `middlewares` applied. */ pipe(middlewares) { return new Task(this.invokable.pipe(middlewares)); } /** * The `pipeWhen` method conditionally applies additional `middlewares`, returning a new `Task` instance only if the specified condition is met. */ pipeWhen(condition, middlewares) { return new Task(this.invokable.pipeWhen(condition, middlewares)); } then(onfulfilled, onrejected) { if (this.promise === null) { this.promise = this.invokable.invoke(); } // eslint-disable-next-line @typescript-eslint/use-unknown-in-catch-callback-variable return this.promise.then(onfulfilled, onrejected); } /** * The `detach` method executes the `Task` without awaiting it. * @example * ```ts * import { Task } from "@daiso-tech/core/task"; * import { TimeSpan } from "@daiso-tech/core/time-span" from "@daiso-tech/core/time-span"; * * const promise = * new Task(async () => { * await Task.delay(TimeSpan.fromSeconds(1)); * // Will be loged after one second * console.log("Done !"); * }); * * promise.detach(); * * // Will be logged immediately * console.log("Hello"); * await Task.delay(TimeSpan.fromSeconds(2)); * ``` */ detach() { this.then(() => { }); } } //# sourceMappingURL=task.js.map