{"id":17089142,"url":"https://github.com/giltayar/nursery","last_synced_at":"2025-04-12T22:10:23.371Z","repository":{"id":33620222,"uuid":"159811706","full_name":"giltayar/nursery","owner":"giltayar","description":"Package implementing concurrency primitive inspired by the blog post \"Notes on Structured Concurrency; or Go statement considered harmful\"","archived":false,"fork":false,"pushed_at":"2023-07-19T01:26:03.000Z","size":460,"stargazers_count":7,"open_issues_count":10,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-30T09:16:15.450Z","etag":null,"topics":["async","concurrency","javascript","library"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/giltayar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-11-30T11:07:35.000Z","updated_at":"2024-12-11T06:23:44.000Z","dependencies_parsed_at":"2023-07-25T00:22:32.958Z","dependency_job_id":null,"html_url":"https://github.com/giltayar/nursery","commit_stats":{"total_commits":72,"total_committers":1,"mean_commits":72.0,"dds":0.0,"last_synced_commit":"f063cc89ca133870100c27d702813453ef423820"},"previous_names":["giltayar/nursery-rhymes","giltayar/promise-nursery"],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giltayar%2Fnursery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giltayar%2Fnursery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giltayar%2Fnursery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/giltayar%2Fnursery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/giltayar","download_url":"https://codeload.github.com/giltayar/nursery/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248637771,"owners_count":21137538,"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","concurrency","javascript","library"],"created_at":"2024-10-14T13:45:48.523Z","updated_at":"2025-04-12T22:10:23.349Z","avatar_url":"https://github.com/giltayar.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# nursery\n\nPackage implementing concurrency primitive inspired by the blog post\n[Notes on structured concurrency, or: Go statement considered harmful](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/)\n\n[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)\n\n## Installing\n\n```sh\nnpm install nursery\n```\n\nThis package requires Node 10 and above, and has only one dependency: `abort-controller` which is used as a polyfill\nfor the standard `AbortController` class used to signal cancellation in the `fetch` API.\n\nWhy v10? Because it's API heavily uses `for await`, which is available only since Node 10.\n\n## Using the package\n\nThe package enables you to group a number of running async tasks into one, and to ensure that they all\nfinish together. Let's see an example:\n\n### Running Multiple Tasks Together\n\n```js\nconst Nursery = require('nursery')\n\nconst delay = ms =\u003e new Promise(resolve =\u003e setTimeout(resolve, ms))\n\n;(async function() {\n  await Nursery([\n    delay(20).then(() =\u003e console.log('second')),\n    delay(10).then(() =\u003e console.log('first'))\n  ])\n  }\n})()\n// ==\u003e first\n// ==\u003e second\n```\n\nHow is this different from the following using `Promise.all`?\n\n```js\n;(async function() {\n  await Promise.all([\n    delay(20).then(() =\u003e console.log('second')),\n    delay(10).then(() =\u003e console.log('first')),\n  ])\n})()\n// ==\u003e first\n// ==\u003e second\n```\n\nIt isn't! Same. But!\n\n### Handling Task Faillures\n\nBut `Promise.all` is dumb. It doesn't include the following guarantee:\n\n\u003e A Nursery waits for all tasks to terminate, **even if one of the tasks fails**.\n\nLet's look at this example:\n\n```js\n;(async function() {\n  try {\n    await Promise.all([\n      Promise.reject(new Error('failed!')),\n      delay(10).then(() =\u003e console.log('first')),\n    ])\n  } catch (err) {\n    console.log('after Promise.all', err.message)\n  }\n})()\n// ==\u003e after Promise.all failed!\n// ==\u003e first\n```\n\nIn the above example, due to the failure of the first task, the Promise.all exits immediately, without waiting\nfor the delay task to end. Thus, the catch happens first and output `after Promise.all failed!`, and then\n**sometime in the future, wihout us having any control about it**, the other task ends.\n\nWhat happened if the other task failed? Can we do anything about it? Nope. It silently fails. We lost control over it.\n\nAnd this is bad. Why this is bad intuitively makes sense, but the blog post\n[Notes on structured concurrency, or: Go statement considered harmful](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/)\nmakes a pretty good case on why this it's bad.\n\nLet's contrast this with the same implementation of the code, using nurseries:\n\n```js\n;(async function() {\n  try {\n    await Nursery([\n      Promise.reject(new Error('failed!')),\n      delay(10).then(() =\u003e console.log('first')),\n    ])\n  } catch (err) {\n    console.log('after Nursery', err.message)\n  }\n})()\n// ==\u003e first\n// ==\u003e after Nursery failed!\n```\n\nThis code works as we \"expect\" it too. The nursery does not finish until all nursery-run\npromises finish, even if one of the tasks fail. We still get the error, but all the tasks end their run.\n\nNote: what happens if _more_ than one task fails? Look it up in the API, this is handled well. TL;DR: the exception\nthrown includes a field that has all the other errors in an array.\n\nLet's look at another way of writing this in `nursery`:\n\n```js\n;(async function() {\n  try {\n    for await (const {nurse} of Nursery()) {\n      nurse(Promise.reject(new Error('failed!')))\n      nurse(delay(10).then(() =\u003e console.log('first')))\n    }\n  } catch (err) {\n    console.log('after Nursery', err.message)\n  }\n})()\n// ==\u003e first\n// ==\u003e after Nursery failed!\n```\n\nThe syntax is strange. There's a `for await` loop, and a body that runs two tasks using `nurse(...)`.\nDon't worry, the `for await` loop executes only once\n(we'll see later that it can execute more if we want, for retries).\nThe tasks run concurrently, but the `for await` loop (along with `Nursery` magic),\nensures that the body of the for loop waits till both tasks have run.\n\nYou can think of the `Nursery(...)` call, because it was called without any tasks, to create an iterator\nof `Nurse`-es that enable executing tasks inside the nursery. Once all the tasks finish executing, the `for await`\nalso finishes.\n\nNote: tasks in `Nursery` are either already-created promises,\nor functions that returns promises that the nursey executes to get the promise. For example, the above\ncode can be written, but instead of passing promises directly, we pass async functions:\n\n```js\n;(async function() {\n  try {\n    for await (const {nurse} of Nursery()) {\n      nurse(() =\u003e Promise.reject(new Error('failed!')))\n      nurse(() =\u003e delay(10).then(() =\u003e console.log('first')))\n    }\n  } catch (err) {\n    console.log('after Nursery', err.message)\n  }\n})()\n// ==\u003e first\n// ==\u003e after Nursery failed!\n```\n\n### Cancelling a Task\n\nBut what if I want to cancel a task if another task fails? I still want to wait till that cancellation is done,\nbut I want to cancel it. As an exanple,\nlet's create a \"real\" task, which uses the [Star Wars API](https://swapi.dev/) to get the height of Luke Skywalker:\n\n```js\nconst fetch = require('node-fetch')\n\nasync function fetchSkywalkerHeight(fetchOptions) {\n  const response = await fetch('https://swapi.dev/api/people/1/', fetchOptions)\n\n  const skywalkerInfo = await response.json()\n  return skywalkerInfo.height\n}\n\n;(async function() {\n  console.log(await fetchSkywalkerHeight())\n})()\n// ==\u003e 172\n```\n\nNote: we'll use the `fetchOptions` later, when cancelling this task.\n\nNow let's use this task in a nursery with another failed task:\n\n```js\n  await (async function() {\n    try {\n      for await (const {nurse} of Nursery()) {\n        nurse(Promise.reject(new Error('failed!')))\n        nurse(fetchSkywalkerHeight().then(height =\u003e console.log(height)))\n      }\n    } catch (err) {\n      console.log('after Nursery', err.message)\n    }\n  })()\n  // ==\u003e 172\n  // ==\u003e after Nursery failed!\n```\n\nThe nursery waits for the `fetchSkywalker` task to terminate, and thus it outputs `172` before failing. How\ncan we abort that fetch? We send the `fetch` an abort signal (this is part of the Fetch API):\n\n```js\nawait (async function() {\n  try {\n    for await (const {nurse, signal} of Nursery()) {\n      nurse(Promise.reject(new Error('failed!')))\n      nurse(fetchSkywalkerHeight({signal}).then(height =\u003e console.log(height)))\n    }\n  } catch (err) {\n    console.log('after Nursery', err.message)\n  }\n})()\n// ==\u003e after Nursery failed!\n```\n\nIn this snippet, the \"172\" was never output, because the `fetchSkywalkerHeight` was cancelled. How? The nursery\nalways creates an `AbortController` (with it's accompanying `AbortSignal`) when initializing,\nand when one of the tasks fails, it calls `AbortController.abort()` to signal to the other tasks to abort.\n\nThe `AbortController` and it's accompanying `AbortSignal` are stored in the nursery as\n`abortController` and `signal` respectively. We pass the `signal` to the Fetch API to tell it when to abort. This\nis how the `fetch` knows how to abort the task once of the other tasks fail.\n\n\u003e I chose `AbortController` as the cancellation API as there is currently no other standard API that enables\n\u003e task cancellation. If JavaScript in the future standardizes on another standard, I'll add that one too.\n\nYou can use the `AbortSignal` to enable your own cancellation mechanism. Let's see an example:\n\n```js\n;(async function() {\n  try {\n    for await (const {nurse, signal} of Nursery()) {\n      nurse(Promise.reject(new Error('failed!')))\n      nurse(\n        delay(10).then(_ =\u003e\n          !signal.aborted ? console.log('not aborted') : console.log('aborted'),\n        ),\n      )\n    }\n  } catch (err) {\n    console.log('after Nursery', err.message)\n  }\n})()\n// ==\u003e aborted\n// ==\u003e after Nursery failed!\n```\n\nWhen an abort happens, the `signal` becomes `true`, enabling us to check the flag and abort whenever we want to.\nWe can also use `signal.addEventListener('abort', ...)` to register an abort handler if we want to.\n\n\u003e For more information on `AbortController`,\n\u003e see [this](https://developer.mozilla.org/en-US/docs/Web/API/AbortController).\n\u003e For more information on the Fetch API use of `AbortController`,\n\u003e see [this](https://developer.mozilla.org/en-US/docs/Web/API/AbortController#Examples).\n\n### Other really cool stuff you can do with a nursery\n\n* **Retrying**: if you pass `{retries: 3}` to the `Nursery` call,\n  the body of the `for await` (or the tasks in the tasks list),\n  are retried 3 trimes (if needed). See \"retries\" section below.\n* **Throttling**: if you pass `{execution: throat(3)}`\n  (using the wonderful [throat](https://www.npmjs.com/package/throat) package)) to the `Nursery` call,\n  the execution of the tasks is throttled to three at a time. See \"execution\" section below.\n* **Sequential execution of async functions**: pass `{execution: throat(1)}` to the `Nursery` call,\n  and you get sequential execution of the async functions in the task list!\n\n### Timers and Supervisors\n\nYou can also run a task in \"supervisor\" mode. A task in this mode is not waited upon. Once all the other\ntasks are done, the nursery closes. Thus a supervisor task can supervise and wait for all other tasks to be done.\n\n(Note that a supervisor tasks is also waited upon to close, as this is a prime directive of a nursery: all tasks end.\nBut this is done in a second phase: after all regular tasks are done, aborting is signalled, which allows supervisors\nto also be finish).\n\nThe simplest task that should be run in supervisor mode is the `Nursery.timeoutTask`. This task enables us to timeout\nall tasks running in a nursery. For example, lets timeout the lukeSkywalker task:\n\n```js\nawait (async function() {\n  try {\n    for await (const {nurse, supervisor} of Nursery()) {\n      supervisor(Nursery.timeoutTask(5))\n      nurse(fetchSkywalkerHeight({signal}).then(height =\u003e console.log(height)))\n    }\n  } catch (err) {\n    if (err instanceof Nursery.TimeoutError) {\n      console.log('Timed out!')\n    }\n  }\n})()\n// ==\u003e Timed out!\n```\n\nWhile `Nursery.timeoutTask` is an important supervisor task, you can write your own in a simple way. Look\nat the [Nursery.timeoutTask source code](./src/timeout-task.js) to understand how to write other supervisor tasks.\n\n## API Reference\n\n```js\nconst Nursery = require('nursery')\n```\n\nThe only export is `Nursery`, a function that if called with a set of \"tasks\" (or just one), returns a promise, thus:\n\n```js\nawait Nursery([...listOfTasks | task]))\n```\n\n\u003e A task is either a `Promise` (e.g. `Promise.resolve(42)` or `funcReturningPromise()`) or\n\u003e a function returning a `Promise` (e.g. `() =\u003e Promise.resolve(42)` or `funcReturningPromise`).\n\nNote: I sometimes refer to a function returning a `Promise` is sometimes referred to as an **async** function.\n\nInstead of an array of tasks, you can pass just one task.\n\nIf no tasks are passed, calling `Nursery` returns a\n[generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) of `Nurse` objects,\ndestined to be used in a\n[`for await` loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of), thus:\n\n```js\nfor await (const nurseObject of Nursery()) {\n  // body using the nurseObject\n}\n```\n\nUnless there are retries (see below on how to ask for retries), the body of the loop will run only 1 time.\n\n## Nursery function\n\nThree overloads:\n\n* `Nursery(task: Promise|Function, [options: object])`\n* `Nursery(taskList: Array\u003cPromise|Function\u003e, [options: object])`\n* `Nursery([options: object])`\n\nThis function call returns a `Promise` if called with a task or an array tasks,\nor returns an **async** generator of `nursery` objects (of type `Nursery`) if not.\nThis generator is commonly used in `for await` loops.\n\n* `taskList`: optional array of tasks. A task is either a `Promise` or a function returning a `Promise`.\n* `options`: [optional] object with the following properties:\n  * `retries`: the number of retries to run the loop body in case of failures. Default: 0. See \"Retries\" section.\n  * `onRetry`: an async function that will be called (and waited upon) between every retry. It is passed\n    the following parameters:\n    * `attempt`: the number of retry attempt it is. Before the first retry, `attempty` is 1,\n      before the second retry, it is `2` and so on.\n      Note that if this function returns a rejected promise, then the retries stop. This enables\n      you to do error filtering and continue with the retries only on the errors you want.\n    * `remaining`: the remaining number of attempts left. Note that `remaining + attempt === retries`.\n    A nice set of retry functions is supplied as part of the `Nursery` object. See below.\n  * `execution`: a function that receives a task that are async function and calls it. The nursery uses it\n    to execute all tasks that are async functions.\n    The default is to call it as is, but you can use it, for example, to throttle execution. See \"execution\" section.\n\n* Returns:\n  * If no `taskLists`: an **async** generator commonly used in `for await` loops.\n    Example (definition of `delay` can be found above):\n\n    ```js\n    for await (const {nurse} of Nursery()) {\n      nurse(delay(10).then(() =\u003e console.log('done')))\n      nurse(delay(20).then(() =\u003e console.log('done')))\n    }\n    ```\n\n    In this example, the `for await` loop will wait until the two delays are done.\n\n  * If `taskList`: a Promise that is exactly what `Promise.all` returns. Example:\n\n    ```js\n    console.log(await Nursery([\n      delay(10).then(() =\u003e 4),\n      delay(20).then(() =\u003e 2),\n    ]))\n    // ==\u003e [4, 2]\n    ```\n\n    In this example, the Nursery call will wait until the two delays are done and will return an array of results\n\n  * If `task`: a Promise that is the return value of the task:\n\n    ```js\n    console.log(await Nursery([\n      delay(10).then(() =\u003e 4),\n    ]))\n    // ==\u003e 4\n    ```\n\n    In this example, the Nursery call will wait until the two delays are done and will return an array of results\n\n    In all the above cases, a `Nurse` object is passed to the function, to enable it to run other tasks. Note that\n    the return value in this case is an array of all the `nurse` run tasks + the return value of the task function\n    itself:\n\n    ```js\n    console.log(\n      await Nursery(({nurse}) =\u003e {\n        nurse(delay(20).then(_ =\u003e 'run1'))\n        nurse(delay(10).then(_ =\u003e 'run2'))\n        return 'done'\n      }),\n    )\n    ```\n\n### Nurse object\n\nThe object generated by the Nursery generator above. In the following example, `nurseryObject` is a nursery object:\n\n```js\nfor await (const nurseryObject of Nursery()) {\n  nurseryObject.nurse(delay(10).then(() =\u003e console.log('done')))\n  nurseryObject.nurse(delay(20).then(() =\u003e console.log('done')))\n}\n```\n\nThe `Nurse` object has the following properties:\n\n* `nurse(task: Promise | function)`:\n  If the task is a `Promise`, it will wait for promise resolution or cancelation. If it is a `function`,\n  it will call the function (with a `nurse` object), and wait on the `Promise` returned by it.\n  If the function is synchronous and does not return a promise,\n  it will transform the sync value (or exception) into a promise automatically.\n\n  If the task is a function, it will be called, and passed itself (the nursery object).\n\n  The `Nursery` generator (or `Nursery` function call) ensures that **all** tasks that\n  ran in the body of the `for await` loop\n  (or were given to the `Nursery` function)\n  terminate by waiting for them.\n\n  The function call returns the `Promise` (either the one given to it, or the one returned by the function). You _can_, but\n  don't have to `await` on the task (because the Nursery generator will wait for it when it is closed by the `for await` loop).\n* `supervisor(task: Promise | function)`: runs the task as a supervisor.\n   A task in this mode is not waited upon. Once all the _other_ (non-supervisor) tasks are done, the nursery closes.\n   Thus a supervisor task can supervise and wait for all other tasks to be done.\n   Note that a supervisor tasks is also waited upon to close, as this is a prime directive of a nursery: all tasks end.\n   But this is done in a second phase: after all regular tasks are done, aborting is signalled, which allows supervisors\n   to register on abort and end.\n* `signal`: an `AbortSignal` (see [here](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)) to enable\n  the tasks running in the nursery to detect when the nursery is aborted. In the example below, the second task\n  detects that the nursery was aborted using `AbortSignal.aborted` in `nurse.signal.aborted ? ... : ...`:\n\n  ```js\n  for await (const {nurse, signal} of Nursery()) {\n    nurse(Promise.reject(new Error('failed!')))\n    nurse(\n      delay(10).then(_ =\u003e\n        !signal.aborted ? console.log('not aborted') : console.log('aborted'),\n      ),\n    )\n  }\n  // ==\u003e aborted\n  // ==\u003e after Nursery failed\n  ```\n\n* `abortController`: the `AbortController` (see [here](https://developer.mozilla.org/en-US/docs/Web/API/AbortController))\n  that can be used to abort the nursery, using `nurse.abortController.abort()`. This will abort the nursery without\n  the need to fail a task. Example:\n\n```js\nfor await (const {nurse, signal, abortController} of Nursery()) {\n  nurse(delay(10).then(() =\u003e abortController.abort()))\n  nurse(\n    delay(10).then(_ =\u003e (!signal.aborted ? console.log('not aborted') : console.log('aborted'))),\n  )\n}\n// ==\u003e aborted\n```\n\n## Closing the Nursery Generator\n\nThe nursery will close itself after at least 1 generation of a nursery object\n(see \"retries\" below for when it is more).\n\nClosing a generator will do the following:\n\n* Wait for all Promises to be either resolved or rejected.\n* If all promises are resolved, then all is good.\n* If only one promise is rejected, it will throw/reject that promise (after waiting as described above).\n* If more than one promise is rejected, it will throw/reject the first promise that was rejected, but will append\n  all other errors to it as a property of that error, in the field designated by the `Symbol` key `Nursery.moreErrors`.\n  Example:\n\n```js\ntry {\n  for await (const {nurse} of Nursery()) {\n    nurse(Promise.reject(new Error('first error')))\n    nurse(delay(10).then(_ =\u003e Promise.reject(new Error('second error'))))\n  }\n} catch (err) {\n  console.log(err.message)\n  console.log(err[Nursery.moreErrors][0].message)\n}\n// ==\u003e first error\n// ==\u003e second error\n```\n\nNote that if the first error is not of type `object`, then no field can or is added to it, but it is still rejected.\n\n## Retries\n\nExample with retries:\n\n```js\nlet rejectionCount = 0\n\nfor await (const {nurse} of Nursery({retries: 1})) {\n  nurse(() =\u003e rejectionCount++ ===  0 ? Promise.reject(new Error()) : Promise.resolve(1))\n  nurse(delay(20).then(() =\u003e console.log('done')))\n}\n```\n\nIn the above example, `done` will be output twice because in the first run, the first task fails, and thus the whole\nbody retries.\n\n## Execution\n\nThe `execution` option enables you to control the execution of tasks that are functions. Note that if a task\nis a `Promise`, it will not be passed through the `execution` option, as it is already executing.\n\nThe nursery will pass all task exections through the function.\n\nIn the example below, we can log each task execution:\n\n```js\nfunction log(f) {\n  console.log('executing task')\n\n  return f()\n}\n\nfor await (const {nurse} of Nursery({execution: log})) {\n  nurse(() =\u003e delay(10).then(_ =\u003e console.log(1)))\n  nurse(() =\u003e delay(20).then(_ =\u003e console.log(2)))\n}\n// ==\u003e executing task\n// ==\u003e executing task\n// ==\u003e 1\n// ==\u003e 2\n```\n\nAnother, more practical example, using the wonderful [throat](https://www.npmjs.com/package/throat) package:\n\n```js\nconst throat = require('throat')\n\n// `throat(1)` returns a function that will execute functions passed to it, as is,\n// but with a concurrency level of 1, i.e. sequential\nfor await (const {nurse} of Nursery({execution: throat(1)})) {\n  nurse(() =\u003e delay(20).then(_ =\u003e console.log(1)))\n  nurse(() =\u003e delay(10).then(_ =\u003e console.log(2)))\n  nurse(() =\u003e delay(5).then(_ =\u003e console.log(3)))\n  nurse(() =\u003e delay(30).then(_ =\u003e console.log(4)))\n}\n\n// =\u003e 1\n// =\u003e 2\n// =\u003e 3\n// =\u003e 4\n```\n\n## Nursery.timeoutTask\n\nA function that returns a task that is supposed to run in supervisor mode,\nand throws a `Nursery.TimeoutError` if the timeout is reached.\n\n* `Nursery.timeoutTask(ms, options)`: returns a task that times out after `ms` milliseconds, or aborts with no error\n  if other non-supervisor tasks are finished.\n  * `ms`: the number of milliseconds to wait to throw the `Nursery.TimeoutError`. The exception will be thrown\n    only if other non-supervisor tasks are not finished.\n  * `options`: an object with the following properties:\n    * `name`: the name of the task. default: `undefined`. Will be sent to the `Nursery.TimeoutError`, which\n      shows it in the error and is also a property of the exception.\n\nExample:\n\n```js\ntry {\n  const [, skyWalkerHeight] = await Nursery(({nurse, supervisor, signal}) =\u003e {\n    supervisor(Nursery.timeoutTask(5, {name: 'fetchSkywalkerHeight'}))\n\n    nurse(fetchSkywalkerHeight({signal}))\n  })\n\n  console.log(skyWalkerHeight)\n} catch (err) {\n  if (err.code === 'ERR_NURSERY_TIMEOUT_ERR') {\n    console.log(err.message)\n  }\n}\n// ==\u003e Timeout of 5ms occured for task fetchSkywalkerHeight\n```\n\nNote in the example how we get back the skywalker height: it is the _second_ value returned because the\ntimeoutTask also returns a value (`undefined`).\n\n## Nursery.TimeoutError\n\nA subclass of `Error`. Is thrown by `Nursery.timeoutTask` when a timeout occurs. Includes the following properties:\n\n* `message`: the regular `Error` message, with a specific message about the timeout, including the `name` and `ms`\n  of the timeout.\n* `code`: will be set to `ERR_NURSERY_TIMEOUT_ERR` to be able to identify this error.\n* `ms`: the number of milliseconds of the timeout (same as the `ms` value of `Nursery.timeoutTask`)\n* `name`: the name of the task ((same as the `name` option of `Nursery.timeoutTask`))\n\nSee `Nursery.timeoutTask` for an example of catching a `Nursery.TimeoutError`.\n\n## Nursery.constantTimeRetry\n\n* A function that returns a timeout function that can be supplied to `Nursery`-s `onRetry`. It creates\n  a constant delay before each retry. It accepts the following parameter:\n  * `delta`: the delay, in milliseconds, before each retry. Example:\n\n```js\nfor await (const {nurse} of Nursery({\n  retries: 3,\n  onRetry: Nursery.constantTimeRetry({delta: 200}),\n})) {\n  // ...\n}\n```\n\n## Nursery.linearTimeRetry\n\n* A function that returns a timeout function that can be supplied to `Nursery`-s `onRetry`. It creates\n  a linear delay before each retry. It accepts the following parameter:\n  * `start`: the initial first delay before each retry.\n  * `delta`: How much to grow each delay before each retry.\n  * `max`: the maximum delay possible. Default: `Infinity`\n\n  Note that the calculcation before each attempt will be `Math.min(start + delta * (attempt - 1), max)`\n  Example:\n\n```js\nfor await (const {nurse} of Nursery({\n  retries: 3,\n  onRetry: Nursery.linearTimeRetry({start: 100, delta: 50}),\n})) {\n  // ...\n}\n```\n\n## Nursery.exponentialTimeRetry\n\n* A function that returns a timeout function that can be supplied to `Nursery`-s `onRetry`. It creates\n  an exponential (backoff) delay before each retry. It accepts the following parameter:\n  * `start`: the initial first delay before each retry.\n  * `factor`: How much to grow each delay before each retry.\n  * `max`: the maximum delay possible. Default: `Infinity`\n\n  Note that the calculcation before each attempt will be `Math.min(start * factor ** (attempt - 1), max)`\n  Example:\n\n```js\nfor await (const {nurse} of Nursery({\n  retries: 3,\n  onRetry: Nursery.exponentialTimeRetry({start: 100, factor: 2}),\n})) {\n  // ...\n}\n```\n\n## Nursery.CancelTask\n\nAn exception, which if thrown _from inside a task_, will cancel it without aborting, and return a value. Example:\n\n```js\nconst [res1, res2] = await Nursery([\n  () =\u003e {\n    // ...\n    throw new Nursery.CancelTask(42)\n  },\n  () =\u003e Promise.resolve(43),\n])\nconsole.log(res1)\nconsole.log(res2)\n\n// ==\u003e 42\n// ==\u003e 43\n```\n\nNote that if you throw this exception, the task will not fail. It's a nice and simple way to abort a task\nwithout the whole Nursery being rejected.\n\n`Nursery.CancelTask` extends JavaScript's `Error` with the following:\n\n* `constructor(value[], message)`:\n  * `value`: the value returned by the task. Will set the property `value`.\n  * `message`: the `this.message`. The default is `Nursery task cancelled`.\n* `code`: used to identify that it is a `CancelTask`. Has the value `ERR_NURSERY_TASK_CANCELLED`\n* `value`: the value to be returned by the task\n\n## Contributing\n\n* Contributions are welcome! PRs are welcome!\n* To build the code, use `npm run build` (currently runs nothing).\n* To test the code, use `npm test`, which will both run the tests under the `test` folder, and run eslint\n* This code uses [prettier](https://github.com/prettier/prettier) and `npm test` verifies that.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiltayar%2Fnursery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgiltayar%2Fnursery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgiltayar%2Fnursery/lists"}