{"id":22763549,"url":"https://github.com/mearns/tracking-promise","last_synced_at":"2025-03-30T09:42:11.312Z","repository":{"id":57378702,"uuid":"195151197","full_name":"mearns/tracking-promise","owner":"mearns","description":"A JavaScript library for tracking the success/failure of a promise without rejecting itself.","archived":false,"fork":false,"pushed_at":"2020-11-20T16:13:10.000Z","size":73,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-03-18T15:00:43.684Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/mearns.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-04T01:51:11.000Z","updated_at":"2020-11-20T16:13:01.000Z","dependencies_parsed_at":"2022-09-05T08:10:35.968Z","dependency_job_id":null,"html_url":"https://github.com/mearns/tracking-promise","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":"mearns/node-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mearns%2Ftracking-promise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mearns%2Ftracking-promise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mearns%2Ftracking-promise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mearns%2Ftracking-promise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mearns","download_url":"https://codeload.github.com/mearns/tracking-promise/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246301955,"owners_count":20755512,"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":[],"created_at":"2024-12-11T11:08:57.911Z","updated_at":"2025-03-30T09:42:11.294Z","avatar_url":"https://github.com/mearns.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tracking-promise\n\nA JavaScript library for tracking the success/failure of a promise without rejecting itself.\n\nA simple use case for tracking is to _trap_ a promise so that you get a fulfillment regardless of whether\nthat promise fulfills or rejects. You can then inspect the results to determine how it settled.\n\nMore generally, you can track promises, immediate values, or functions that return either.\nThis gives you access to a `Tracker` object that tracks information about the current\nstate: whether or not it is finished, whether it succeeded (returned or fulfilled) or failed\n(threw or rejected), and what value or error it finished with.\n\nThe `Tracker` object also serves as a promise (a _thenable_) which will fulfill (always) when\nthe tracked value finishes, regardless of how it finishes.\n\n## Related\n\n### The _Try_ Pattern\n\nIf all you actually care about is making sure you get a fulfillment regardless of how the promise settles,\nyou might want to consider using the Try pattern instead, for instance using\n[`Try.fromPromise`](https://mearns.github.io/fp-try/classes/try.html#frompromise) from the\n[fp-try](https://www.npmjs.com/package/fp-try) package.\n\n### `Promise.allSettled`\n\nThis is conceptually similar to the [`Promise.allSettled`](https://github.com/tc39/proposal-promise-allSettled) function slated for inclusion in ES2020, in that both will fulfill regardless of how the given promise settles. As of version 1.1, this package fulfills\nwith a value that is compatible with the outcome objects provided by `allSettled`.\n\nThe `allSettled` function would\nessentially be a shortcut for `Promise.all(promises.map(track))`, except for the following limitations of `allSettled`:\n\n1. `Promise.allSettled` does not directly support timeout.\n2. `Promise.allSettled` does not provide information to distinguish between promises and immediate values.\n3. `Promise.allSettled` has no polling mechanism (i.e., the `finished` field provided by the tracker).\n\nMany use cases won't need any of these things and so `Promise.allSettled` may work fine for you.\n\n## Overview\n\nInstall however you install npm packages, e.g.:\n\n```console\nnpm install --save tracking-promise\n```\n\nExample use case:\n\n```javascript\nfunction functionToTrack() {\n    /* ... */\n}\n\nconst TIMEOUT_MS = 1000;\n\nconst track = require(\"track\");\n\nasync function main() {\n    // The `track` function will invoke the given function synchronously,\n    // and return a \"Tracker\" object for it.\n    // the timeout argument is optional, and has some important limitations!\n    const tracker = track(functionToTrack, TIMEOUT_MS);\n\n    // Some tracker fields are always set immediately (synchronously) and are available\n    // upong returning from `track`.\n    tracker.finished; // has the tracked value ended\n    tracker.synchronous; // is the tracked value is synchronous or async\n\n    // A Tracker is a promise, it fulfills when the tracked value has ended\n    const results = await tracker;\n\n    // It fulfills with information about the tracked value:\n    results.synchronous; // was the tracked value synchronous or async?\n    results.failed; // did the tracked object reject or throw?\n    results.error; // if failed, what was the error?\n    results.value; // if succeeded, what was the resulting value?\n    results.timedout; // Did the tracked value timeout (if a timeout was given)?\n\n    // Once the tracked value is finished, the Tracker has all the same\n    // fields set as well.\n}\n```\n\n## Tracked Values\n\nYou can pass any of the following in as the first argument to the `track` function:\n\n1. An async function (a function that returns a promise)\n2. A synchronous function (a function that returns an immediate/non-promise value, or that synchronously throws an error)\n3. A promise (or any other _thenable_)\n4. An immediate (non-promise) value.\n\nWhen a function is given (options 1 and 2), it is invoked synchronously inside the `track` function,\nwith any thrown errors being caught.\n\nOptions 2 and 4 are considered _synchronous_, while 1 and 3 are _asynchronous_.\n\nSynchronous values are considered to be _finished_ immediately (i.e., by the time the `track` function returns).\nIf a synchronous function (option 2) throws an error, it is considered _failed_ with that _error_. Otherwise, its\n_value_ is the value it returns. An immediate value (option 4) cannot fail, and it is considered to be its own _value_.\n\nAsynchronous values are considered to be _finished_ when the promise settles (either fulfills or rejects). Either\nasync option is considered _failed_ if and only if the promise rejects, and its _error_ is whatever value it rejects\nwith. Otherwise, it's _value_ is whatever value it fulfills with.\n\n## Fields\n\nThe following fields are available on the `Tracker` object returned by a call to `track`. Values that are set upon return from `track` may\nsubsequently change values for asynchronous tracked values. No additional changes will be made once `finished`\nis set to `true`.\n\nValues that are not set upon return from `track` are present but explicitly set to `undefined`.\n\nAll of these fields except for `finished` are also present on the object that the `Tracker` promise fulfills with\n(finished is not needed because the fact that `Tracker` has fulfilled implies that it is finished).\n\n| Field Name             | Set upon Return from `track` | Immediate Values     | Synchronous Functions                                        | Promises and Async Functions                                                |\n| ---------------------- | ---------------------------- | -------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------- |\n| `finished`             | yes                          | always `true`        | always `true`                                                | `false` until promise settles, then `true`                                  |\n| `synchronous`          | yes                          | always `true`        | always `true`                                                | always `false`                                                              |\n| `value` †              | for synchronous only         | the value itself     | the returned value                                           | the value the promise fulfills with                                         |\n| `reason` ‡             | for synchronous only         | never set            | the thrown error                                             | the value the promise rejects with                                          |\n| `error` ‡ \\*           | for synchronous only         | never set            | the thrown error                                             | the value the promise rejects with                                          |\n| `failed`               | for synchronous only         | always `false`       | `true` if function throws, otherwise `false`                 | `true` if the promise rejects, otherwise `false`                            |\n| `status`               | for synchronous only         | always `\"fulfilled\"` | `\"rejected\"` if the function throws, otherwise `\"fulfilled\"` | `\"rejected\"` if the promise rejects, otherwise `\"fulfilled\"`.               |\n| `timedout`\u003csup\u003e§\u003c/sup\u003e | for synchronous only         | always `false`       | always `false`                                               | `true` if a timeout argument is given and the promise times out\u003csup\u003e§\u003c/sup\u003e |\n\n† - Only given when `failed` is false, otherwise explicitly set to `undefined`. \u003cbr /\u003e\n‡ - Only given when `failed` is true, otherwise explicitly set to `undefined`. \u003cbr /\u003e \\\n\\* - Note the the `error` field is entirely redundant with the `reason` field; it was superceded by the latter in version 1.1 of this package in order to align with `Promise.allSettled`,\nhowever both fields will remain in order to maintain compatibility. \u003cbr /\u003e\n§ - See \"Timeouts\" below for details.\n\n## Timeouts\n\nThe optional second argument to the `track` function is a timeout, in milliseconds. Timeouts have the following\nlimitations:\n\n1. Timeouts only apply to asynchronous tracked value (promises, and functions that return promises). Synchronous\n   tracked values will never be marked as _timedout_, regardless of how long a synchronous function runs for.\n2. Timeouts do not have any affect on already executing jobs: there is no cancel or abort or anything else.\n3. There's no way to guarantee that the `Tracker` will settle within any finite amount of time.\n4. There's no way to guarantee that the `track` function will return within any finite amount of time.\n\nThe last two limitations are a consequence of the JavaScript execution engine. In short: JavaScript is single\nthreaded and there is no pre-emption. The soonest any job can execute is when all jobs that already existed on\nthe queue have completed. If your asynchronus function pushes a long-running job to the job-queue, no other job\ncan run until this one has completed, including the promise-handler jobs that will settle the `Tracker` (hence point\n3).\n\nAdditionally, the given function is actually executed synchronously inside the `track` invocation (that's the only\nway we can know if it's going to return a promise or not). Thus even if it's an \"asynchronous\" function, but does\na lot of synchronous work before returning the promise, the `track` function won't continue or finish before your\nfunction finishes its asynchronous work (hence point 4).\n\nNone of this should be too surprising if you're generally familiar with how timers work in JavaScript, and the\nsubtleties generally won't matter. For when they do, this is the general flow of the `track` function (the parts\nrelevant to timeout):\n\n1. A timer `T` is started.\n2. If the tracked value is a function, it's invoked synchronously\n3. If the tracked value is a promise, or a function that returns a promise, onfulfill and onreject handlers are\n   synchronously registered with it (by calling it's `then` method).\n4. After timer `T` expires, it's callback will check if either promise handler has executed yet: if they have\n   then the timer callback does nothing; if they have not then the timer fulfills the `Tracker` promise, marking it\n   as a timeout.\n5. When either promise handler is invoked, if the timer callback has already executed the\n   handlers do nothing. Otherwise, the handler will cancel timer `T` and fulfill the `Tracker` promise appropriately\n   (not as a timeout).\n\n## API\n\nFor the most part, you will just treat a `Tracker` as a thennable for the tracked results; either `await`-ing the `Tracker`\nor using the `then(...)` method. The `Tracker` also provides the following common convenience Promise methods:\n\n| Promise-like method                     | use                                                                                                   |\n| --------------------------------------- | ----------------------------------------------------------------------------------------------------- |\n| `then(fulfillHandler, [rejectHandler])` | The standard handler register for a thennable / promise                                               |\n| `catch(rejectHandler)`                  | A convenience method for registering a reject-handler                                                 |\n| `finally(finallyHandler)`               | A convenience method for registering a handler that will not transform the value of the promise chain |\n\nBecause the Tracker always fulfills, a reject-handler will never be called, and the `catch` method is actually just\na near-empty function that returns the Tracker itself.\n\nNote that, as typical, the `finally` method will return a promise for the same value as what the Tracker itself\nfulfills with; it is used for side effects, not for transforming the promise chain. However, if the finallyHandler\nthrows or returns a promise that rejects, than the returned promise will reject with the same error.\n\n### Unpacking\n\nYou can use the `unpack` method to get a promise that fulfills or rejects according to whether or not the tracked\nthing succeeds or fails, regardless of what type of thing was being tracked. It the tracked thing succeeds, then\nthe unpacked promise will fulfill with the `value`; if the tracked thing fails, then then unpacked promise\nwill reject with the `reason`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmearns%2Ftracking-promise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmearns%2Ftracking-promise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmearns%2Ftracking-promise/lists"}