{"id":13869793,"url":"https://github.com/jsr-core/asyncutil","last_synced_at":"2025-12-11T21:10:12.794Z","repository":{"id":46578328,"uuid":"337935731","full_name":"jsr-core/asyncutil","owner":"jsr-core","description":"Asynchronous primitive utility pack","archived":false,"fork":false,"pushed_at":"2024-09-24T19:39:32.000Z","size":177,"stargazers_count":25,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-05T07:40:03.488Z","etag":null,"topics":["async","javascript","jsr","typescript"],"latest_commit_sha":null,"homepage":"https://jsr.io/@core/asyncutil","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/jsr-core.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":["lambdalisue"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"custom":null}},"created_at":"2021-02-11T05:11:39.000Z","updated_at":"2025-04-26T20:40:29.000Z","dependencies_parsed_at":"2023-12-22T02:54:31.124Z","dependency_job_id":"b67f522b-8325-4e5f-853f-2a0d7924b34b","html_url":"https://github.com/jsr-core/asyncutil","commit_stats":{"total_commits":28,"total_committers":4,"mean_commits":7.0,"dds":0.2142857142857143,"last_synced_commit":"de55089a09262f4155204b9d72aee578d4472ac7"},"previous_names":["jsr-core/asyncutil"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/jsr-core/asyncutil","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsr-core%2Fasyncutil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsr-core%2Fasyncutil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsr-core%2Fasyncutil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsr-core%2Fasyncutil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jsr-core","download_url":"https://codeload.github.com/jsr-core/asyncutil/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jsr-core%2Fasyncutil/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27670264,"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","status":"online","status_checked_at":"2025-12-11T02:00:11.302Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","javascript","jsr","typescript"],"created_at":"2024-08-05T20:01:17.363Z","updated_at":"2025-12-11T21:10:12.773Z","avatar_url":"https://github.com/jsr-core.png","language":"TypeScript","funding_links":["https://github.com/sponsors/lambdalisue"],"categories":["TypeScript"],"sub_categories":[],"readme":"# asyncutil\n\n[![JSR](https://jsr.io/badges/@core/asyncutil)](https://jsr.io/@core/asyncutil)\n[![Test](https://github.com/jsr-core/asyncutil/actions/workflows/test.yml/badge.svg)](https://github.com/jsr-core/asyncutil/actions/workflows/test.yml)\n[![Codecov](https://codecov.io/github/jsr-core/asyncutil/graph/badge.svg?token=pfbLRGU5AM)](https://codecov.io/github/jsr-core/asyncutil)\n\nAsynchronous primitive utility pack.\n\n## Usage\n\n### AsyncValue\n\n`AsyncValue` is a class that wraps a value and allows it to be set\nasynchronously.\n\n```ts\nimport { assertEquals } from \"@std/assert\";\nimport { AsyncValue } from \"@core/asyncutil/async-value\";\n\nconst v = new AsyncValue(0);\nassertEquals(await v.get(), 0);\nawait v.set(1);\nassertEquals(await v.get(), 1);\n```\n\n### Barrier\n\n`Barrier` is a synchronization primitive that allows multiple tasks to wait\nuntil all of them have reached a certain point of execution before continuing.\n\n```ts\nimport { Barrier } from \"@core/asyncutil/barrier\";\n\nconst barrier = new Barrier(3);\n\nasync function worker(id: number) {\n  console.log(`worker ${id} is waiting`);\n  await barrier.wait();\n  console.log(`worker ${id} is done`);\n}\n\nworker(1);\nworker(2);\nworker(3);\n```\n\n### ensurePromise\n\n`ensurePromise` is a utility function that ensures a value is a promise.\n\n```ts\nimport { ensurePromise } from \"@core/asyncutil/ensure-promise\";\n\nconst p1 = ensurePromise(Promise.resolve(\"Resolved promise\"));\nconsole.log(await p1); // Resolved promise\n\nconst p2 = ensurePromise(\"Not a promise\");\nconsole.log(await p2); // Not a promise\n```\n\n### flushPromises\n\n`flushPromises` flushes all pending promises in the microtask queue.\n\n```ts\nimport { flushPromises } from \"@core/asyncutil/flush-promises\";\n\nlet count = 0;\nArray.from({ length: 5 }).forEach(() =\u003e {\n  Promise.resolve()\n    .then(() =\u003e count++)\n    .then(() =\u003e count++);\n});\n\nconsole.log(count); // 0\nawait flushPromises();\nconsole.log(count); // 10\n```\n\n### Lock/RwLock\n\n`Lock` is a mutual exclusion lock that provides safe concurrent access to a\nshared value.\n\n```ts\nimport { AsyncValue } from \"@core/asyncutil/async-value\";\nimport { Lock } from \"@core/asyncutil/lock\";\n\n// Critical section\nconst count = new Lock(new AsyncValue(0));\nawait count.lock(async (count) =\u003e {\n  const v = await count.get();\n  count.set(v + 1);\n});\n```\n\n`RwLock` is a reader-writer lock implementation that allows multiple concurrent\nreads but only one write at a time. Readers can acquire the lock simultaneously\nas long as there are no writers holding the lock. Writers block all other\nreaders and writers until the write operation completes.\n\n```ts\nimport { AsyncValue } from \"@core/asyncutil/async-value\";\nimport { RwLock } from \"@core/asyncutil/rw-lock\";\n\nconst count = new RwLock(new AsyncValue(0));\n\n// rlock should allow multiple readers at a time\nawait Promise.all(\n  [...Array(10)].map(() =\u003e {\n    return count.rlock(async (count) =\u003e {\n      console.log(await count.get());\n    });\n  }),\n);\n\n// lock should allow only one writer at a time\nawait Promise.all(\n  [...Array(10)].map(() =\u003e {\n    return count.lock(async (count) =\u003e {\n      const v = await count.get();\n      console.log(v);\n      count.set(v + 1);\n    });\n  }),\n);\n```\n\n### Mutex\n\n`Mutex` is a mutex (mutual exclusion) is a synchronization primitive that grants\nexclusive access to a shared resource.\n\nThis is a low-level primitive. Use `Lock` instead of `Mutex` if you need to\naccess a shared value concurrently.\n\n```ts\nimport { AsyncValue } from \"@core/asyncutil/async-value\";\nimport { Mutex } from \"@core/asyncutil/mutex\";\n\nconst count = new AsyncValue(0);\n\nasync function doSomething() {\n  const v = await count.get();\n  await count.set(v + 1);\n}\n\nconst mu = new Mutex();\n\n// Critical section\n{\n  using _lock = await mu.acquire();\n  await doSomething();\n}\n```\n\n### Notify\n\n`Notify` is an async notifier that allows one or more \"waiters\" to wait for a\nnotification.\n\n```ts\nimport { assertEquals } from \"@std/assert\";\nimport { promiseState } from \"@core/asyncutil/promise-state\";\nimport { Notify } from \"@core/asyncutil/notify\";\n\nconst notify = new Notify();\nconst waiter1 = notify.notified();\nconst waiter2 = notify.notified();\nnotify.notify();\nassertEquals(await promiseState(waiter1), \"fulfilled\");\nassertEquals(await promiseState(waiter2), \"pending\");\nnotify.notify();\nassertEquals(await promiseState(waiter1), \"fulfilled\");\nassertEquals(await promiseState(waiter2), \"fulfilled\");\n```\n\n### peekPromiseState\n\n`peekPromiseState` is used to determine the state of the promise. Mainly for\ntesting purpose.\n\n```typescript\nimport { peekPromiseState } from \"@core/asyncutil/peek-promise-state\";\n\nconst p1 = Promise.resolve(\"Resolved promise\");\nconsole.log(await peekPromiseState(p1)); // fulfilled\n\nconst p2 = Promise.reject(\"Rejected promise\").catch(() =\u003e undefined);\nconsole.log(await peekPromiseState(p2)); // rejected\n\nconst p3 = new Promise(() =\u003e undefined);\nconsole.log(await peekPromiseState(p3)); // pending\n```\n\nUse `flushPromises` to wait all pending promises to resolve.\n\n```typescript\nimport { flushPromises } from \"@core/asyncutil/flush-promises\";\nimport { peekPromiseState } from \"@core/asyncutil/peek-promise-state\";\n\nconst p = Promise.resolve\u003cvoid\u003e(undefined)\n  .then(() =\u003e {})\n  .then(() =\u003e {});\n\nconsole.log(await peekPromiseState(p)); // pending\nawait flushPromises();\nconsole.log(await peekPromiseState(p)); // fulfilled\n```\n\n### Queue/Stack\n\n`Queue` is a queue implementation that allows for adding and removing elements,\nwith optional waiting when popping elements from an empty queue.\n\n```ts\nimport { assertEquals } from \"@std/assert\";\nimport { Queue } from \"@core/asyncutil/queue\";\n\nconst queue = new Queue\u003cnumber\u003e();\nqueue.push(1);\nqueue.push(2);\nqueue.push(3);\nassertEquals(await queue.pop(), 1);\nassertEquals(await queue.pop(), 2);\nassertEquals(await queue.pop(), 3);\n```\n\n`Stack` is a stack implementation that allows for adding and removing elements,\nwith optional waiting when popping elements from an empty stack.\n\n```ts\nimport { assertEquals } from \"@std/assert\";\nimport { Stack } from \"@core/asyncutil/stack\";\n\nconst stack = new Stack\u003cnumber\u003e();\nstack.push(1);\nstack.push(2);\nstack.push(3);\nassertEquals(await stack.pop(), 3);\nassertEquals(await stack.pop(), 2);\nassertEquals(await stack.pop(), 1);\n```\n\n### Semaphore\n\nA semaphore that allows a limited number of concurrent executions of an\noperation.\n\n```ts\nimport { Semaphore } from \"@core/asyncutil/semaphore\";\n\nconst sem = new Semaphore(5);\nconst worker = () =\u003e {\n  return sem.lock(async () =\u003e {\n    // do something\n  });\n};\nawait Promise.all([...Array(10)].map(() =\u003e worker()));\n```\n\n### WaitGroup\n\n`WaitGroup` is a synchronization primitive that enables promises to coordinate\nand synchronize their execution. It is particularly useful in scenarios where a\nspecific number of tasks must complete before the program can proceed.\n\n```ts\nimport { delay } from \"@std/async/delay\";\nimport { WaitGroup } from \"@core/asyncutil/wait-group\";\n\nconst wg = new WaitGroup();\n\nasync function worker(id: number) {\n  wg.add(1);\n  console.log(`worker ${id} is waiting`);\n  await delay(100);\n  console.log(`worker ${id} is done`);\n  wg.done();\n}\n\nworker(1);\nworker(2);\nworker(3);\nawait wg.wait();\n```\n\n## License\n\nThe code follows MIT license written in [LICENSE](./LICENSE). Contributors need\nto agree that any modifications sent in this repository follow the license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsr-core%2Fasyncutil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjsr-core%2Fasyncutil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjsr-core%2Fasyncutil/lists"}