{"id":22613390,"url":"https://github.com/tomas2d/promise-based-task","last_synced_at":"2025-04-11T09:19:02.131Z","repository":{"id":42526117,"uuid":"442291459","full_name":"Tomas2D/promise-based-task","owner":"Tomas2D","description":"Create a task and resolve it later via a Promise approach. Run time-consuming processes only once.","archived":false,"fork":false,"pushed_at":"2025-04-03T01:03:54.000Z","size":1340,"stargazers_count":8,"open_issues_count":4,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-06T03:56:03.923Z","etag":null,"topics":["async","async-task","promise","task-map","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Tomas2D.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["Tomas2D"]}},"created_at":"2021-12-27T22:51:40.000Z","updated_at":"2025-03-29T08:39:47.000Z","dependencies_parsed_at":"2023-09-24T11:33:27.239Z","dependency_job_id":"82875f8b-45bf-4fa4-95b9-bfa7c4e35f7f","html_url":"https://github.com/Tomas2D/promise-based-task","commit_stats":{"total_commits":161,"total_committers":2,"mean_commits":80.5,"dds":0.2360248447204969,"last_synced_commit":"a1dd95b14c81467e98dcdf0dc167659aaf6a9731"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tomas2D%2Fpromise-based-task","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tomas2D%2Fpromise-based-task/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tomas2D%2Fpromise-based-task/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tomas2D%2Fpromise-based-task/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Tomas2D","download_url":"https://codeload.github.com/Tomas2D/promise-based-task/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248366915,"owners_count":21092063,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","async-task","promise","task-map","typescript"],"created_at":"2024-12-08T17:16:50.175Z","updated_at":"2025-04-11T09:19:02.113Z","avatar_url":"https://github.com/Tomas2D.png","language":"TypeScript","funding_links":["https://github.com/sponsors/Tomas2D"],"categories":[],"sub_categories":[],"readme":"# 🛤 promise-based-task\n\n[![CI](https://github.com/Tomas2D/promise-based-task/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/Tomas2D/promise-based-task/actions/workflows/node.js.yml)\n[![codecov](https://codecov.io/gh/Tomas2D/promise-based-task/branch/main/graph/badge.svg?token=SQA7VM6XIV)](https://codecov.io/gh/Tomas2D/promise-based-task)\n\nWith this tiny library, you will be able to solve situations where you want to prevent  \nmultiple requests to do time-consuming operations simultaneously.\nWith this approach, one request will take care of creating the final data and other processes\nwill asynchronously wait for the completion. \n\n**NOTE:** This library is useful if you need a lightweight solution without an extra service like Redis / PubSub etc.\n\nDoes this library help you? Please give it a ⭐️!\n\n## ✨️ Features\n\n- Later resolving of a task in a `Promise` way\n- Auto rejecting promises removed from the shared data structure (`TaskMap`, which is an extension of a `Map` data structure)\n- Zero dependencies\n- Map with limited space (sliding-window)\n- Task Queue mechanism\n\n## 🚀 Installation\n\n```\nyarn add promise-based-task\n```\n```\nnpm install promise-based-task\n```\n\n## 🤘🏻 Usage\n\n**Single task**\n```typescript\nimport { Task } from 'promise-based-task'\n\nlet pricesTask: Task\u003cnumber[]\u003e | null = null\n\napp.get('/prices', async function (req, res) {\n  if (pricesTask === null) {\n    pricesTask = new Task\u003cnumber[]\u003e();\n\n    const result = await longRunningTask()\n    pricesTask.resolve(result)\n  }\n\n  const prices = await pricesTask\n  res.json(prices)\n})\n```\n\n**Multiple tasks (without removing)**\n\nThis type is useful when you want to cache data that are not so huge, but\ntheir creation is expensive. Following code prevents multiple requests to react the\ncritical section of generating the data.\n\n```typescript\nimport { Task, TaskMap } from 'promise-based-task'\n\nlet pricesTasks: TaskMap\u003cstring, number[]\u003e = new TaskMap\u003cstring, number[]\u003e()\n\napp.get('/prices/:date', async function (req, res) {\n  const date = req.params.date\n\n  if (!pricesTasks.has(date)) {\n    const task = new Task\u003cnumber[]\u003e();\n    pricesTasks.set(date, task)\n\n    const result = await longRunningTask(date)\n    task.resolve(result)\n  }\n\n  const prices = await pricesTasks.get(date)\n  res.json(prices)\n})\n```\n\n**Multiple tasks (with removing)**\n\nSometimes you do not want to store results in memory because results can be enormous in size\nand thus, your server can run out of his memory pretty fast.\n\nFollowing code prevents multiple requests to trigger the process of transforming data to\ndesired shape and uploading them to S3.\n\n```typescript\nimport { Task, TaskMap } from 'promise-based-task'\n\nconst tasks: TaskMap\u003cstring, void\u003e = new TaskMap\u003cstring, void\u003e()\n\napp.get('/observations/:date', async function (req, res) {\n  const date = req.params.date\n\n  if (tasks.has(date)) {\n    await tasks.get(date)\n  } else {\n    const task = new Task\u003cvoid\u003e()\n    tasks.set(date, task)\n\n    const rawData = await fetchData(date)\n    const data = await analyzeData(rawData)\n    await uploadDataToS3(data)\n\n    task.resolve()\n    tasks.delete(date)\n  }\n\n  downloadDataFromS3(date).pipe(res)\n})\n```\n\n**Task Queue**\n\n```typescript\n// Type Definition\ndeclare class TaskQueue\u003cT\u003e {\n  constructor(queueSize?: number); // default is 1\n  clear(): Promise\u003cvoid\u003e;\n  execute(fn: () =\u003e Promise\u003cT\u003e): Promise\u003cT\u003e;\n  getStats(): {\n    processingCount: number;\n    waitingCount: number;\n  };\n}\n```\n\nYou want to use the Task Queue when you want to throttle requests/executions of given part of your system.\n\n```typescript\nconst queue = new TaskQueue\u003cIScrapeResult\u003e(5)\n\napp.get('/scrape/:source', async function (req, res) {\n  const source = req.params.source\n  const response = await queue.execute(async () =\u003e {\n    // this function will run by the queue mechanism\n    return service.scrape(source)\n  })\n  \n  res.json(response)\n})\n\napp.get('/scrape/status', async function (req, res) {\n  const { processingCount, waitingCount } = queue.getStats()\n  \n  return res.json({\n    processingCount,\n    waitingCount,\n  })\n})\n```\n\n**Sliding window**\n```typescript\n// Type Definition\ndeclare class SlidingTaskMap\u003cK, V extends Deletable\u003e extends TaskMap\u003cK, V\u003e {\n  constructor(windowSize: number, ttl?: number);\n  set(key: K, value: V, customTTL?: number): this; // create/override \n  delete(key: K): boolean; // remove single entry\n  clear(): void;  // removes all entries\n  pop(): boolean; // removes oldest entry, false for an empty window\n  shift(): boolean;  // removes newest entry, false for an empty window\n}\n```\n\nWhen your map size reaches a specified threshold, the oldest values will be\nremoved. You can be then sure that the size of the map will never overflow your memory.\n\n```typescript\nimport { Task, SlidingTaskMap } from 'promise-based-task'\n\nconst WINDOW_SIZE = 10\nconst TTL = 60 * 1000 // data will persist 60 seconds in a cache\nconst tasks = new SlidingTaskMap\u003cstring, number[]\u003e(WINDOW_SIZE, TTL)\n\napp.get('/calculation/:date', async function () {\n  const date = req.params.date\n\n  if (!tasks.has(date)) {\n    const task = new Task\u003cvoid\u003e()\n    tasks.set(date, task)\n\n    const data = await fetchData(date)\n    task.resolve(data)\n  }\n\n  return tasks.get(date)\n})\n```\n\nWhen your map size reaches a specified threshold, the oldest values will be\nremoved. You can be then sure that the size of the map will never overflow your memory.\n\n```typescript\nimport { Task, SlidingTaskMap } from 'promise-based-task'\n\nconst WINDOW_SIZE = 10\nconst TTL = 60 * 1000 // data will persist 60 seconds in a cache\nconst tasks = new SlidingTaskMap\u003cstring, number[]\u003e(WINDOW_SIZE, TTL)\n\napp.get('/calculation/:date', async function () {\n  const date = req.params.date\n\n  if (!tasks.has(date)) {\n    const task = new Task\u003cvoid\u003e()\n    tasks.set(date, task)\n    \n    const data = await fetchData(date)\n    task.resolve(data)\n  }\n  \n  return tasks.get(date)\n})\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomas2d%2Fpromise-based-task","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomas2d%2Fpromise-based-task","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomas2d%2Fpromise-based-task/lists"}