{"id":20367569,"url":"https://github.com/rubenverborgh/asynciterator","last_synced_at":"2025-03-17T13:11:21.090Z","repository":{"id":23631883,"uuid":"27001639","full_name":"RubenVerborgh/AsyncIterator","owner":"RubenVerborgh","description":"An asynchronous iterator library for advanced object pipelines in JavaScript","archived":false,"fork":false,"pushed_at":"2024-07-16T13:45:43.000Z","size":2401,"stargazers_count":49,"open_issues_count":25,"forks_count":7,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-03-05T04:11:56.958Z","etag":null,"topics":["async","asynchronous","iterator","javascript","typescript"],"latest_commit_sha":null,"homepage":"https://rubenverborgh.github.io/AsyncIterator/docs/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"jd-alexander/Google-Directions-Android","license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RubenVerborgh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","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":"2014-11-22T14:43:22.000Z","updated_at":"2024-12-10T04:55:19.000Z","dependencies_parsed_at":"2023-11-24T15:30:26.964Z","dependency_job_id":"4e277f4a-16c1-4693-af52-2f459b9c4ffd","html_url":"https://github.com/RubenVerborgh/AsyncIterator","commit_stats":{"total_commits":277,"total_committers":8,"mean_commits":34.625,"dds":"0.12635379061371843","last_synced_commit":"3171521759194b3ce463f989d8253e35978ef690"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubenVerborgh%2FAsyncIterator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubenVerborgh%2FAsyncIterator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubenVerborgh%2FAsyncIterator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubenVerborgh%2FAsyncIterator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RubenVerborgh","download_url":"https://codeload.github.com/RubenVerborgh/AsyncIterator/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244039206,"owners_count":20387834,"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","asynchronous","iterator","javascript","typescript"],"created_at":"2024-11-15T00:33:20.966Z","updated_at":"2025-03-17T13:11:21.068Z","avatar_url":"https://github.com/RubenVerborgh.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Asynchronous iterators for JavaScript\n[![Build Status](https://github.com/RubenVerborgh/AsyncIterator/actions/workflows/ci.yml/badge.svg)](https://github.com/RubenVerborgh/AsyncIterator/actions)\n[![Coverage Status](https://coveralls.io/repos/github/RubenVerborgh/AsyncIterator/badge.svg)](https://coveralls.io/github/RubenVerborgh/AsyncIterator)\n[![npm version](https://badge.fury.io/js/asynciterator.svg)](https://www.npmjs.com/package/asynciterator)\n\n**AsyncIterator is a lightweight JavaScript implementation of demand-driven object streams,**\nand an alternative to the two-way flow controlled [Node.js `Stream`](https://nodejs.org/api/stream.html).\nAs opposed to `Stream`, you cannot _push_ anything into an `AsyncIterator`;\ninstead, an iterator _pulls_ things from another iterator.\nThis eliminates the need for expensive, complex flow control.\n\n[Read the full API documentation.](http://rubenverborgh.github.io/AsyncIterator/docs/)\n\n## Data streams that only generate what you need\n`AsyncIterator` allows functions to\n**return multiple _asynchronously_ and _lazily_ created values**.\nThis adds a missing piece to JavaScript,\nwhich natively supports returning a single value synchronously\nand asynchronously (through [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)),\nbut multiple values only synchronously (through [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)):\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u0026nbsp;\u003c/td\u003e\n    \u003cth\u003esingle value\u003c/th\u003e\n    \u003cth\u003emultiple values\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth\u003esynchronous\u003c/th\u003e\n    \u003ctd\u003e\u003ccode\u003eT getValue()\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ccode\u003eIterable\u0026lt;T\u0026gt; getValues()\u003c/code\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth\u003easynchronous\u003c/th\u003e\n    \u003ctd\u003e\u003ccode\u003ePromise\u0026lt;T\u0026gt; getValue()\u003c/code\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003cstrong\u003e\u003ccode\u003eAsyncIterator\u0026lt;T\u0026gt; getValues()\u003c/code\u003e\u003c/strong\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nLike `Iterable`, **an `AsyncIterator` only generates items when you ask it to**.\nThis contrast with patterns such as [`Observable`](http://reactivex.io/intro.html),\nwhich are data-driven and don't wait for consumers to process items.\n\n### The asynchronous iterator interface\nAn asynchronous iterator is an object that exposes a series of data items by:\n- implementing [`EventEmitter`](https://nodejs.org/api/events.html#events_class_eventemitter)\n- returning an item when you call `iterator.read` (yielding `null` when none is available at the moment)\n- informing when new items might be available through `iterator.on('readable', callback)`\n- informing when no more items will become available through `iterator.on('end', callback)`\n- streaming all of its items when you register through `iterator.on('data', callback)`\n\nAny object that conforms to the above conditions can be used with the AsyncIterator library\n(this includes [Node.js Streams](https://nodejs.org/api/stream.html)).\nThe `AsyncIterator` interface additionally exposes\n[several other methods and properties](http://rubenverborgh.github.io/AsyncIterator/docs/AsyncIterator.html).\n\n## Example: fetching Wikipedia links related to natural numbers\nIn the example below, we create an iterator of links found on Wikipedia pages for natural numbers.\n```JavaScript\nimport https from 'https';\nimport { resolve } from 'url';\nimport { IntegerIterator } from 'asynciterator';\n\n// Iterate over the natural numbers\nconst numbers = new IntegerIterator({ start: 0, end: Infinity });\n// Transform these numbers into Wikipedia URLs\nconst urls = numbers.map(n =\u003e `https://en.wikipedia.org/wiki/${n}`);\n// Fetch each corresponding Wikipedia page\nconst pages = urls.transform((url, done, push) =\u003e {\n  https.get(url, response =\u003e {\n    let page = '';\n    response.on('data', data =\u003e { page += data; });\n    response.on('end',  () =\u003e { push(page); done(); });\n  });\n});\n// Extract the links from each page\nconst links = pages.transform((page, done, push) =\u003e {\n  let search = /href=\"([^\"]+)\"/g, match;\n  while (match = search.exec(page))\n    push(resolve('https://en.wikipedia.org/', match[1]));\n  done();\n});\n```\n\nWe could display a link every 0.1 seconds:\n```JavaScript\nsetInterval(() =\u003e {\n  const link = links.read();\n  if (link)\n    console.log(link);\n}, 100);\n```\n\nOr we can get the first 30 links and display them:\n```JavaScript\nlinks.take(30).on('data', console.log);\n```\n\nIn both cases, pages from Wikipedia will only be fetched when needed—the data consumer is in control.\nThis is what makes `AsyncIterator` [lazy](https://en.wikipedia.org/wiki/Lazy_evaluation).\n\nIf we had implemented this using the `Observable` pattern,\nan entire flow of unnecessary pages would be fetched,\nbecause it is controlled by the data publisher instead.\n\n## Usage\n`AsyncIterator` implements the [`EventEmitter`](https://nodejs.org/api/events.html#events_class_eventemitter) interface\nand a superset of the [`Stream`](https://nodejs.org/api/stream.html) interface.\n\n### Consuming an AsyncIterator in on-demand mode\nBy default, an AsyncIterator is in _on-demand_ mode,\nmeaning it only generates items when asked to.\n\nThe [`read` method](http://rubenverborgh.github.io/AsyncIterator/docs/AsyncIterator.html#read) returns the next item,\nor `null` when no item is available.\n\n```JavaScript\nconst numbers = new IntegerIterator({ start: 1, end: 2 });\nconsole.log(numbers.read()); // 1\nconsole.log(numbers.read()); // 2\nconsole.log(numbers.read()); // null\n```\n\nIf you receive `null`,\nyou should wait until the next [`readable` event](http://rubenverborgh.github.io/AsyncIterator/docs/AsyncIterator.html#.event:readable) before reading again.\nThis event is not a guarantee that an item _will_ be available.\n\n```JavaScript\nlinks.on('readable', () =\u003e {\n  let link;\n  while (link = links.read())\n    console.log(link);\n});\n```\n\nThe [`end` event](http://rubenverborgh.github.io/AsyncIterator/docs/AsyncIterator.html#.event:end) is emitted after you have read the last item from the iterator.\n\n### Consuming an AsyncIterator in flow mode\nAn AsyncIterator can be switched to _flow_ mode by listening to the [`data` event](http://rubenverborgh.github.io/AsyncIterator/docs/AsyncIterator.html#.event:data).\nIn flow mode, iterators generate items as fast as possible.\n\n```JavaScript\nconst numbers = new IntegerIterator({ start: 1, end: 100 });\nnumbers.on('data', number =\u003e console.log('number', number));\nnumbers.on('end',  () =\u003e console.log('all done!'));\n```\n\nTo switch back to on-demand mode, simply remove all `data` listeners.\n\n### Setting and reading properties\nAn AsyncIterator can have custom properties assigned to it,\nwhich are preserved when the iterator is cloned.\nThis is useful to pass around metadata about the iterator.\n\n```JavaScript\nconst numbers = new IntegerIterator();\nnumbers.setProperty('rate', 1234);\nconsole.log(numbers.getProperty('rate')); // 1234\n\nconst clone = numbers.clone();\nconsole.log(clone.getProperty('rate'));   // 1234\n\nnumbers.setProperty('rate', 4567);\nconsole.log(clone.getProperty('rate'));   // 4567\n```\n\nYou can also attach a callback\nthat will be called as soon as the property is set:\n\n```JavaScript\nconst numbers = new IntegerIterator();\nnumbers.getProperty('later', console.log);\nnumbers.setProperty('later', 'value');\n// 'value'\n```\n\n### Consuming an AsyncIterator as EcmaScript-AsyncIterator\nDue to the syntactical sugar [EcmaScript's AsyncIterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncIterator)\nprovides, our iterators can also be consumed as such.\nIf high performance over large iterators is required, this method of consumption not recommended.\n\n```JavaScript\nconst numbers = new IntegerIterator({ start: 1, end: 100 });\n\nfor await (const number of numbers)\n  console.log('number', number);\nconsole.log('all done!');\n```\n\nError events emitted within the iterator can be caught by wrapping the for-await-block in a try-catch.\n\nIn cases where the returned EcmaScript AsyncIterator will not be fully consumed,\nit is recommended to manually listen for error events on the main AsyncIterator\nto avoid uncaught error messages.\n\n## License\nThe asynciterator library is copyrighted by [Ruben Verborgh](http://ruben.verborgh.org/)\nand released under the [MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubenverborgh%2Fasynciterator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frubenverborgh%2Fasynciterator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubenverborgh%2Fasynciterator/lists"}