{"id":23789998,"url":"https://github.com/blackglory/extra-promise","last_synced_at":"2025-09-08T16:31:50.124Z","repository":{"id":44797134,"uuid":"96877397","full_name":"BlackGlory/extra-promise","owner":"BlackGlory","description":"🌳 Utilities for JavaScript Promise and async functions.","archived":false,"fork":false,"pushed_at":"2025-01-15T06:38:03.000Z","size":1829,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-25T03:54:20.952Z","etag":null,"topics":["browser","esm","library","nodejs","npm-package","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/extra-promise","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/BlackGlory.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"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}},"created_at":"2017-07-11T09:43:31.000Z","updated_at":"2025-01-15T06:38:04.000Z","dependencies_parsed_at":"2024-01-05T03:32:16.903Z","dependency_job_id":"f474aef8-c09e-4e1f-936a-21c787fe0cdb","html_url":"https://github.com/BlackGlory/extra-promise","commit_stats":{"total_commits":396,"total_committers":2,"mean_commits":198.0,"dds":0.02020202020202022,"last_synced_commit":"75f53e9b05007eda157c759a35103c3210294054"},"previous_names":[],"tags_count":138,"template":false,"template_full_name":null,"purl":"pkg:github/BlackGlory/extra-promise","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Fextra-promise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Fextra-promise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Fextra-promise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Fextra-promise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BlackGlory","download_url":"https://codeload.github.com/BlackGlory/extra-promise/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BlackGlory%2Fextra-promise/sbom","scorecard":{"id":22494,"data":{"date":"2025-08-11","repo":{"name":"github.com/BlackGlory/extra-promise","commit":"64c554a269388bb6a63c2716ad57cc595c3df64c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.5,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/nodejs.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/BlackGlory/extra-promise/nodejs.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/nodejs.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/BlackGlory/extra-promise/nodejs.yml/master?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/nodejs.yml:21","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"16 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-7q7g-4xm8-89cq","Warn: Project is vulnerable to: GHSA-xffm-g5w8-qvg7","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-vg6x-rcgg-rjx6","Warn: Project is vulnerable to: GHSA-x574-m823-4x7w","Warn: Project is vulnerable to: GHSA-4r4m-qw57-chr8","Warn: Project is vulnerable to: GHSA-xcj6-pq6g-qj4x","Warn: Project is vulnerable to: GHSA-356w-63v5-8wf4","Warn: Project is vulnerable to: GHSA-859w-5945-r5v3","Warn: Project is vulnerable to: GHSA-9crc-q9x8-hgqq"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-14T17:07:54.731Z","repository_id":44797134,"created_at":"2025-08-14T17:07:54.731Z","updated_at":"2025-08-14T17:07:54.731Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274213531,"owners_count":25242491,"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-09-08T02:00:09.813Z","response_time":121,"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":["browser","esm","library","nodejs","npm-package","typescript"],"created_at":"2025-01-01T17:17:57.375Z","updated_at":"2025-09-08T16:31:49.349Z","avatar_url":"https://github.com/BlackGlory.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# extra-promise\nUtilities for JavaScript `Promise` and async functions.\n\n## Install\n```sh\nnpm install --save extra-promise\n# or\nyarn add extra-promise\n```\n\n## API\n```ts\ninterface INonBlockingChannel\u003cT\u003e {\n  send(value: T): void\n  receive(): AsyncIterable\u003cT\u003e\n  close: () =\u003e void\n}\n\ninterface IBlockingChannel\u003cT\u003e {\n  send(value: T): Promise\u003cvoid\u003e\n  receive(): AsyncIterable\u003cT\u003e\n  close: () =\u003e void\n}\n\ninterface IDeferred\u003cT\u003e {\n  resolve(value: T): void\n  reject(reason: unknown): void\n}\n```\n\n### functions\n#### isPromise\n```ts\nfunction isPromise\u003cT\u003e(val: unknown): val is Promise\u003cT\u003e\nfunction isntPromise\u003cT\u003e(val: T): val is Exclude\u003cT, Promise\u003cunknown\u003e\u003e\n```\n\n#### isPromiseLike\n```ts\nfunction isPromiseLike\u003cT\u003e(val: unknown): val is PromiseLike\u003cT\u003e\nfunction isntPromiseLike\u003cT\u003e(val: T): val is Exclude\u003cT, PromiseLike\u003cunknown\u003e\u003e\n```\n\n#### delay\n```ts\nfunction delay(timeout: number, signal?: AbortSignal): Promise\u003cvoid\u003e\n```\n\nA simple wrapper for `setTimeout`.\n\n#### timeout\n```ts\nfunction timeout(ms: number, signal?: AbortSignal): Promise\u003cnever\u003e\n```\n\nIt throws a `TimeoutError` after `ms` milliseconds.\n\n```ts\ntry {\n  result = await Promise.race([\n    fetchData()\n  , timeout(5000)\n  ])\n} catch (e) {\n  if (e instanceof TimeoutError) ...\n}\n```\n\n#### pad\n```ts\nfunction pad\u003cT\u003e(ms: number, fn: () =\u003e Awaitable\u003cT\u003e): Promise\u003cT\u003e\n```\n\nRun a function, but wait at least `ms` milliseconds before returning.\n\n#### parallel\n```ts\nfunction parallel(\n  tasks: Iterable\u003c() =\u003e Awaitable\u003cunknown\u003e\u003e\n, concurrency: number = Infinity\n): Promise\u003cvoid\u003e\n```\n\nPerform tasks in parallel.\n\nThe value range of `concurrency` is [1, Infinity].\nInvalid values will throw `Error`.\n\n#### parallelAsync\n```ts\nfunction parallelAsync(\n  tasks: AsyncIterable\u003c() =\u003e Awaitable\u003cunknown\u003e\u003e\n, concurrency: number // concurrency must be finite number\n): Promise\u003cvoid\u003e\n```\n\nSame as `parallel`, but `tasks` is an `AsyncIterable`.\n\n#### series\n```ts\nfunction series(\n  tasks: Iterable\u003c() =\u003e Awaitable\u003cunknown\u003e\u003e\n       | AsyncIterable\u003c() =\u003e Awaitable\u003cunknown\u003e\u003e\n): Promise\u003cvoid\u003e\n```\n\nPerform tasks in order.\nEquivalent to `parallel(tasks, 1)`.\n\n#### waterfall\n```ts\nfunction waterfall\u003cT\u003e(\n  tasks: Iterable\u003c(result: unknown) =\u003e Awatiable\u003cunknown\u003e\u003e\n       | AsyncIterable\u003c(result: unknown) =\u003e Awaitable\u003cunknown\u003e\u003e\n): Promise\u003cT | undefined\u003e\n```\n\nPerform tasks in order, the return value of the previous task will become the parameter of the next task. If `tasks` is empty, return `Promise\u003cundefined\u003e`.\n\n#### each\n```ts\nfunction each(\n  iterable: Iterable\u003cT\u003e\n, fn: (element: T, i: number) =\u003e Awaitable\u003cunknown\u003e\n, concurrency: number = Infinity\n): Promise\u003cvoid\u003e\n```\n\nThe async `each` operator for Iterable.\n\nThe value range of `concurrency` is [1, Infinity].\nInvalid values will throw `Error`.\n\n#### eachAsync\n```ts\nfunction eachAsync\u003cT\u003e(\n  iterable: AsyncIterable\u003cT\u003e\n, fn: (element: T, i: number) =\u003e Awaitable\u003cunknown\u003e\n, concurrency: number // concurrency must be finite number\n): Promise\u003cvoid\u003e\n```\n\nSame as `each`, but `iterable` is an `AsyncIterable`.\n\n#### map\n```ts\nfunction map\u003cT, U\u003e(\n  iterable: Iterable\u003cT\u003e\n, fn: (element: T, i: number) =\u003e Awaitable\u003cU\u003e\n, concurrency: number = Infinity\n): Promise\u003cU[]\u003e\n```\n\nThe async `map` operator for Iterable.\n\nThe value range of `concurrency` is [1, Infinity].\nInvalid values will throw `Error`.\n\n#### mapAsync\n```ts\nfunction mapAsync\u003cT, U\u003e(\n  iterable: AsyncIterable\u003cT\u003e\n, fn: (element: T, i: number) =\u003e Awaitable\u003cU\u003e\n, concurrency: number // concurrency must be finite number\n): Promise\u003cU[]\u003e\n```\n\nSame as `map`, but `iterable` is an `AsyncIterable`.\n\n#### filter\n```ts\nfunction filter\u003cT, U = T\u003e(\n  iterable: Iterable\u003cT\u003e\n, fn: (element: T, i: number) =\u003e Awaitable\u003cboolean\u003e\n, concurrency: number = Infinity\n): Promise\u003cU[]\u003e\n```\n\nThe async `filter` operator for Iterable.\n\nThe value range of `concurrency` is [1, Infinity].\nInvalid values will throw `Error`.\n\n#### filterAsync\n```ts\nfunction filterAsync\u003cT, U = T\u003e(\n  iterable: AsyncIterable\u003cT\u003e\n, fn: (element: T, i: number) =\u003e Awaitable\u003cboolean\u003e\n, concurrency: number // concurrency must be finite number\n): Promise\u003cU[]\u003e\n```\n\nSame as `filter`, but `iterable` is an `AsyncIterable`.\n\n#### all\n```ts\nfunction all\u003cT extends { [key: string]: PromiseLike\u003cunknown\u003e }\u003e(\n  obj: T\n): Promise\u003c{ [Key in keyof T]: UnpackedPromiseLike\u003cT[Key]\u003e }\u003e\n```\n\nIt is similar to `Promise.all`, but the first parameter is an object.\n\n```ts\nconst { task1, task2 } = await all({\n  task1: invokeTask1()\n, task2: invokeTask2()\n})\n```\n\n#### promisify\n```ts\ntype Callback\u003cT\u003e = (err: any, result?: T) =\u003e void\n\nfunction promisify\u003cResult, Args extends any[] = unknown[]\u003e(\n  fn: (...args: [...args: Args, callback?: Callback\u003cResult\u003e]) =\u003e unknown\n): (...args: Args) =\u003e Promise\u003cResult\u003e\n```\n\nThe well-known `promisify` function.\n\n#### callbackify\n```ts\ntype Callback\u003cT\u003e = (err: any, result?: T) =\u003e void\n\nfunction callbackify\u003cResult, Args extends any[] = unknown[]\u003e(\n  fn: (...args: Args) =\u003e Awaitable\u003cResult\u003e\n): (...args: [...args: Args, callback: Callback\u003cResult\u003e]) =\u003e void\n```\n\nThe `callbackify` function, as opposed to `promisify`.\n\n#### asyncify\n```ts\nfunction asyncify\u003cArgs extends any[], Result, This = unknown\u003e(\n  fn: (this: This, ...args: Args) =\u003e Awaitable\u003cResult\u003e\n): (this: This, ...args: Promisify\u003cArgs\u003e) =\u003e Promise\u003cResult\u003e\n```\n\nTurn sync functions into async functions.\n\n```ts\nconst a = 1\nconst b = Promise.resolve(2)\n\nconst add = (a: number, b: number) =\u003e a + b\n\n// BAD\nadd(a, await b) // 3\n\n// GOOD\nconst addAsync = asyncify(add) // (a: number | PromiseLike\u003cnumber\u003e, b: number | PromiseLike\u003cnumber\u003e) =\u003e Promise\u003cnumber\u003e\nawait addAsync(a, b) // Promise\u003c3\u003e\n```\n\nIt can also be used to eliminate the call stack:\n```ts\n// OLD\nfunction count(n: number, i: number = 0): number {\n  if (i \u003c n) return count(n, i + 1)\n  return i\n}\n\ncount(10000) // RangeError: Maximum call stack size exceeded\n\n// NEW\nconst countAsync = asyncify((n: number, i: number = 0): Awaitable\u003cnumber\u003e =\u003e {\n  if (i \u003c n) return countAsync(n, i + 1)\n  return i\n})\n\nawait countAsync(10000) // 10000\n```\n\n#### spawn\n```ts\nfunction spawn\u003cT\u003e(\n  num: number\n, create: (id: number) =\u003e Awaitable\u003cT\u003e\n): Promise\u003cT[]\u003e\n```\n\nA sugar for create multiple values in parallel.\n\nThe parameter `id` is from `1` to `num`.\n\n#### limitConcurrencyByQueue\n```ts\nfunction limitConcurrencyByQueue\u003cT, Args extends any[]\u003e(\n  concurrency: number\n, fn: (...args: Args) =\u003e PromiseLike\u003cT\u003e\n): (...args: Args) =\u003e Promise\u003cT\u003e\n```\n\nLimit the number of concurrency, calls that exceed the number of concurrency will be delayed in order.\n\n#### reusePendingPromises\n```ts\ntype VerboseResult\u003cT\u003e = [value: T, isReuse: boolean]\n\ninterface IReusePendingPromisesOptions\u003cArgs\u003e {\n  createKey?: (args: Args) =\u003e unknown\n  verbose?: true\n}\n\nfunction reusePendingPromises\u003cT, Args extends any[]\u003e(\n  fn: (...args: Args) =\u003e PromiseLike\u003cT\u003e\n, options: IReusePendingPromisesOptions\u003cArgs\u003e \u0026 { verbose: true }\n): (...args: Args) =\u003e Promise\u003cVerboseResult\u003cT\u003e\u003e\nfunction reusePendingPromises\u003cT, Args extends any[]\u003e(\n  fn: (...args: Args) =\u003e PromiseLike\u003cT\u003e\n, options: IReusePendingPromisesOptions\u003cArgs\u003e \u0026 { verbose: false }\n): (...args: Args) =\u003e Promise\u003cT\u003e\nfunction reusePendingPromises\u003cT, Args extends any[]\u003e(\n  fn: (...args: Args) =\u003e PromiseLike\u003cT\u003e\n, options: Omit\u003cIReusePendingPromisesOptions\u003cArgs\u003e, 'verbose'\u003e\n): (...args: Args) =\u003e Promise\u003cT\u003e\nfunction reusePendingPromises\u003cT, Args extends any[]\u003e(\n  fn: (...args: Args) =\u003e PromiseLike\u003cT\u003e\n): (...args: Args) =\u003e Promise\u003cT\u003e\n```\n\nReturns a function that will return the same `Promise` for calls with the same parameters if the `Promise` is pending.\n\nIt generates cache keys based on the `options.createKey` function,\nThe default value of `options.createKey` is a stable `JSON.stringify` implementation.\n\n### Classes\n#### StatefulPromise\n```ts\nenum StatefulPromiseState {\n  Pending = 'pending'\n, Fulfilled = 'fulfilled'\n, Rejected = 'rejected'\n}\n\nclass StatefulPromise\u003cT\u003e extends Promise\u003cT\u003e {\n  static from\u003cT\u003e(promise: PromiseLike\u003cT\u003e): StatefulPromise\u003cT\u003e\n\n  get state(): StatefulPromiseState\n\n  constructor(\n    executor: (\n      resolve: (value: T) =\u003e void\n    , reject: (reason: any) =\u003e void\n    ) =\u003e void\n  )\n\n  isPending(): boolean\n  isFulfilled(): boolean\n  isRejected(): boolean\n}\n```\n\nA subclass of `Promise` used for testing, helps you understand the state of `Promise`.\n\n#### Channel\n```ts\nclass Channel\u003cT\u003e implements IBlockingChannel\u003cT\u003e\n```\n\nImplement MPMC(multi-producer, multi-consumer) FIFO queue communication with `Promise` and `AsyncIterable`.\n\n- `send`\n  Send value to the channel, block until data is taken out by the consumer.\n- `receive`\n  Receive value from the channel.\n- `close`\n  Close the channel.\n\nIf the channel closed, `send` and `receive` will throw `ChannelClosedError`.\n`AsyncIterator` that have already been created do not throw `ChannelClosedError`,\nbut return `{ done: true }`.\n\n```ts\nconst chan = new Channel\u003cstring\u003e()\nqueueMicrotask(() =\u003e {\n  await chan.send('hello')\n  await chan.send('world')\n})\nfor await (const value of chan.receive()) {\n  console.log(value)\n}\n```\n\n#### BufferedChannel\n```ts\nclass BufferedChannel\u003cT\u003e implements IBlockingChannel\u003cT\u003e {\n  constructor(bufferSize: number)\n}\n```\n\nImplement MPMC(multi-producer, multi-consumer) FIFO queue communication with `Promise` and `AsyncIterable`.\nWhen the amount of data sent exceeds `bufferSize`, `send` will block until data in buffer is taken out by the consumer.\n\n- `send`\n  Send value to the channel.\n  If the buffer is full, block.\n- `receive`\n  Receive value from the channel.\n- `close`\n  Close the channel.\n\nIf the channel closed, `send` and `receive` will throw `ChannelClosedError`.\n`AsyncIterator` that have already been created do not throw `ChannelClosedError`,\nbut return `{ done: true }`.\n\n```ts\nconst chan = new BufferedChannel\u003cstring\u003e(1)\n\nqueueMicrotask(() =\u003e {\n  await chan.send('hello')\n  await chan.send('world')\n})\n\nfor await (const value of chan.receive()) {\n  console.log(value)\n}\n```\n\n#### UnlimitedChannel\n```ts\nclass UnlimitedChannel\u003cT\u003e implements INonBlockingChannel\u003cT\u003e\n```\n\nImplement MPMC(multi-producer, multi-consumer) FIFO queue communication with `Promise` and `AsyncIterable`.\n\n`UnlimitedChannel` return a tuple includes three channel functions:\n- `send`\n  Send value to the channel.\n  There is no size limit on the buffer, all sending will return immediately.\n- `receive`\n  Receive value from the channel.\n- `close`\n  Close the channel.\n\nIf the channel closed, `send` and `receive` will throw `ChannelClosedError`.\n`AsyncIterator` that have already been created do not throw `ChannelClosedError`,\nbut return `{ done: true }`.\n\n```ts\nconst chan = new UnlimitedChannel\u003cstring\u003e()\n\nqueueMicrotask(() =\u003e {\n  chan.send('hello')\n  chan.send('world')\n})\n\nfor await (const value of chan.receive()) {\n  console.log(value)\n}\n```\n\n#### Deferred\n```ts\nclass Deferred\u003cT\u003e implements PromiseLike\u003cT\u003e, IDeferred\u003cT\u003e\n```\n\n`Deferred` is a `Promise` that separates `resolve()` and `reject()` from the constructor.\n\n#### MutableDeferred\n```ts\nclass MutableDeferred\u003cT\u003e implements PromiseLike\u003cT\u003e, IDefrred\u003cT\u003e\n```\n\n`MutableDeferred` is similar to `Deferred`,\nbut its `resolve()` and `reject()` can be called multiple times to change the value.\n\n```ts\nconst deferred = new MutableDeferred()\ndeferred.resolve(1)\ndeferred.resolve(2)\n\nawait deferred // resolved(2)\n```\n\n#### ReusableDeferred\n```ts\nclass ReusableDeferred\u003cT\u003e implements PromiseLike\u003cT\u003e, IDeferred\u003cT\u003e\n```\n\n`ReusableDeferred` is similar to `MutableDeferred`,\nbut its internal `Deferred` will be overwritten with a new pending `Deferred` after each call.\n\n```ts\nconst deferred = new ReusableDeferred()\ndeferred.resolve(1)\nqueueMicrotask(() =\u003e deferred.resolve(2))\n\nawait deferred // pending, resolved(2)\n```\n\n#### DeferredGroup\n```ts\nclass DeferredGroup\u003cT\u003e implements IDeferred\u003cT\u003e {\n  add(deferred: IDeferred\u003cT\u003e): void\n  remove(deferred: IDeferred\u003cT\u003e): void\n  clear(): void\n}\n```\n\n#### LazyPromise\n```ts\nclass LazyPromise\u003cT\u003e implements PromiseLike\u003cT\u003e {\n  then: PromiseLike\u003cT\u003e['then']\n\n  constructor(\n    executor: (resolve: (value: T) =\u003e void\n  , reject: (reason: any) =\u003e void) =\u003e void\n  )\n}\n```\n\n`LazyPromise` constructor is the same as `Promise`.\n\nThe difference with `Promise` is that `LazyPromise` only performs `executor` after `then` method is called.\n\n#### Semaphore\n```ts\ntype Release = () =\u003e void\n\nclass Semaphore {\n  constructor(count: number)\n\n  acquire(): Promise\u003cRelease\u003e\n  acquire\u003cT\u003e(handler: () =\u003e Awaitable\u003cT\u003e): Promise\u003cT\u003e\n}\n```\n\n#### Mutex\n```ts\ntype Release = () =\u003e void\n\nclass Mutex extends Semaphore {\n  acquire(): Promise\u003cRelease\u003e\n  acquire\u003cT\u003e(handler: () =\u003e Awaitable\u003cT\u003e): Promise\u003cT\u003e\n}\n```\n\n#### DebounceMicrotask\n```ts\nclass DebounceMicrotask {\n  queue(fn: () =\u003e void): void\n  cancel(fn: () =\u003e void): boolean\n}\n```\n\n`queue` can create a microtask,\nif the microtask is not executed, multiple calls will only queue it once.\n\n`cancel` can cancel a microtask before it is executed.\n\n#### DebounceMacrotask\n```ts\nclass DebounceMacrotask {\n  queue(fn: () =\u003e void): void\n  cancel(fn: () =\u003e void): boolean\n}\n```\n\n`queue` can create a macrotask,\nif the macrotask is not executed, multiple calls will only queue it once.\n\n`cancel` can cancel a macrotask before it is executed.\n\n#### TaskRunner\n```ts\nclass TaskRunnerDestroyedError extends CustomError {}\n\nclass TaskRunner {\n  constructor(\n    concurrency: number = Infinity\n  , rateLimit?: {\n      duration: number\n      limit: number\n    }\n  )\n\n  /**\n   * @throws {TaskRunnerDestroyedError}\n   */\n  run(task: (signal: AbortSignal) =\u003e Awaitable\u003cT\u003e, signal?: AbortSignal): Promise\u003cT\u003e\n\n  destroy(): void\n}\n```\n\nA task runner, it will execute tasks in FIFO order.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblackglory%2Fextra-promise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblackglory%2Fextra-promise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblackglory%2Fextra-promise/lists"}