{"id":13527092,"url":"https://github.com/tc39/proposal-cancellation","last_synced_at":"2025-10-18T23:53:48.379Z","repository":{"id":41450016,"uuid":"87842037","full_name":"tc39/proposal-cancellation","owner":"tc39","description":"Proposal for a Cancellation API for ECMAScript","archived":false,"fork":false,"pushed_at":"2021-04-09T09:08:50.000Z","size":184,"stargazers_count":270,"open_issues_count":11,"forks_count":12,"subscribers_count":65,"default_branch":"master","last_synced_at":"2025-04-13T11:08:33.264Z","etag":null,"topics":["cancellation","ecmascript","javascript","proposal"],"latest_commit_sha":null,"homepage":"https://tc39.github.io/proposal-cancellation","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tc39.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":"2017-04-10T18:06:50.000Z","updated_at":"2025-04-01T23:17:35.000Z","dependencies_parsed_at":"2022-08-29T16:40:40.862Z","dependency_job_id":null,"html_url":"https://github.com/tc39/proposal-cancellation","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-cancellation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-cancellation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-cancellation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tc39%2Fproposal-cancellation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tc39","download_url":"https://codeload.github.com/tc39/proposal-cancellation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248703198,"owners_count":21148118,"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":["cancellation","ecmascript","javascript","proposal"],"created_at":"2024-08-01T06:01:40.897Z","updated_at":"2025-10-18T23:53:48.323Z","avatar_url":"https://github.com/tc39.png","language":"HTML","funding_links":[],"categories":["HTML"],"sub_categories":[],"readme":"# ECMAScript Cancellation\n\nThis proposal seeks to define an approach to user-controlled cancellation of asynchronous operations\nthrough the adoption of a set of native platform objects.\n\n## Status\n\n**Stage:** 1  \n**Champion:** Ron Buckton (@rbuckton), Brian Terlson (@bterlson), Domenic Denicola (@domenic), Yehuda Katz (@wycats)\n\n_For more information see the [TC39 proposal process](https://tc39.github.io/process-document/)._\n\n\u003e NOTE: TC39 has decided to investigate a cancellation mechanism in the core library.\n\u003e As such, Cancellation has moved to Stage 1, but **not** in the form of the previous Stage 0 proposal.\n\u003e Instead, TC39 believes this is a space that requires further investigation and discussion.\n\u003e The previous Stage 0 proposal can be found [here](stage0/README.md).\n\n## Authors\n\n* Ron Buckton (@rbuckton)\n\n# Motivations\n\n* A clear and consistent approach to cancelling asynchronous operations:\n  * Asynchronous functions or [iterators](https://github.com/tc39/proposal-async-iteration)\n  * [Fetching](https://fetch.spec.whatwg.org/#fetch-api) remote resources (HTTP, I/O, etc.)\n  * Interacting with background tasks (Web Workers, forked processes, etc.)\n  * Long-running operations ([animations](https://w3c.github.io/web-animations/), etc.)\n* A general-purpose coordination mechanism with many use cases:\n  * Synchronous observation (e.g. in a game loop)\n  * Asynchronous observation (e.g. aborting an XMLHttpRequest, stopping an animation)\n  * Easy to use in async functions.\n* A common API that is reusable in multiple host environments (Browser, NodeJS, IoT/embedded, etc.).\n\n# Prior Art\n\n* [Cancellation in Managed Threads](https://msdn.microsoft.com/en-us/library/dd997364(v=vs.110)) in the .NET Framework\n\n## Architecture\nThe following are some architectural observations provided by **Dean Tribble** on the [es-discuss mailing list](https://mail.mozilla.org/pipermail/es-discuss/2015-March/041887.html):\n\n\u003e *Cancel requests, not results*  \n\u003e Promises are like object references for async; any particular promise might\n\u003e be returned or passed to more than one client. Usually, programmers would\n\u003e be surprised if a returned or passed in reference just got ripped out from\n\u003e under them *by another client*. this is especially obvious when considering\n\u003e a library that gets a promise passed into it. Using \"cancel\" on the promise\n\u003e is like having delete on object references; it's dangerous to use, and\n\u003e unreliable to have used by others.\n\u003e\n\u003e *Cancellation is heterogeneous*  \n\u003e It can be misleading to think about canceling a single activity. In most\n\u003e systems, when cancellation happens, many unrelated tasks may need to be\n\u003e cancelled for the same reason. For example, if a user hits a stop button on\n\u003e a large incremental query after they see the first few results, what should\n\u003e happen?\n\u003e\n\u003e - the async fetch of more query results should be terminated and the\n\u003e connection closed\n\u003e - background computation to process the remote results into renderable\n\u003e form should be stopped\n\u003e - rendering of not-yet rendered content should be stopped. this might\n\u003e include retrieval of secondary content for the items no longer of interest\n\u003e (e.g., album covers for the songs found by a complicated content search)\n\u003e - the animation of \"loading more\" should be stopped, and should be\n\u003e replaced with \"user cancelled\"\n\u003e - etc.\n\u003e\n\u003e Some of these are different levels of abstraction, and for any non-trivial\n\u003e application, there isn't a single piece of code that can know to terminate\n\u003e all these activities. This kind of system also requires that cancellation\n\u003e support is consistent across many very different types of components. But\n\u003e if each activity takes a cancellationToken, in the above example, they just\n\u003e get passed the one that would be cancelled if the user hits stop and the\n\u003e right thing happens.\n\u003e\n\u003e *Cancellation should be smart*  \n\u003e Libraries can and should be smart about how they cancel. In the case of an\n\u003e async query, once the result of a query from the server has come back, it\n\u003e may make sense to finish parsing and caching it rather than just\n\u003e reflexively discarding it. In the case of a brokerage system, for example,\n\u003e the round trip to the servers to get recent data is the expensive part.\n\u003e Once that's been kicked off and a result is coming back, having it\n\u003e available in a local cache in case the user asks again is efficient. If the\n\u003e application spawned another worker, it may be more efficient to let the\n\u003e worker complete (so that you can reuse it) rather than abruptly terminate\n\u003e it (requiring discarding of the running worker and cached state).\n\u003e\n\u003e *Cancellation is a race*  \n\u003e In an async system, new activities may be getting continuously scheduled by\n\u003e asks that are themselves scheduled but not currently running. The act of\n\u003e cancelling needs to run in this environment. When cancel starts, you can\n\u003e think of it as a signal racing out to catch up with all the computations\n\u003e launched to achieve the now-cancelled objective. Some of those may choose\n\u003e to complete (see the caching example above). Some may potentially keep\n\u003e launching more work before that work itself gets signaled (yeah it's a bug\n\u003e but people write buggy code). In an async system, cancellation is not\n\u003e prompt. Thus, it's infeasible to ask \"has cancellation finished?\" because\n\u003e that's not a well defined state. Indeed, there can be code scheduled that\n\u003e should and does not get cancelled (e.g., the result processor for a pub/sub\n\u003e system), but that schedules work that will be cancelled (parse the\n\u003e publication of an update to the now-cancelled query).\n\u003e\n\u003e *Cancellation is \"don't care\"*  \n\u003e Because smart cancellation sometimes doesn't stop anything and in an async\n\u003e environment, cancellation is racing with progress, it is at most \"best\n\u003e efforts\". When a set of computations are cancelled, the party canceling the\n\u003e activities is saying \"I no longer care whether this completes\". That is\n\u003e importantly different from saying \"I want to prevent this from completing\".\n\u003e The former is broadly usable resource reduction. The latter is only\n\u003e usefully achieved in systems with expensive engineering around atomicity\n\u003e and transactions. It was amazing how much simpler cancellation logic\n\u003e becomes when it's \"don't care\".\n\u003e\n\u003e *Cancellation requires separation of concerns*  \n\u003e In the pattern where more than one thing gets cancelled, the source of the\n\u003e cancellation is rarely one of the things to be cancelled. It would be a\n\u003e surprise if a library called for a cancellable activity (load this image)\n\u003e cancelled an unrelated server query just because they cared about the same\n\u003e cancellation event. I find it interesting that the separation between\n\u003e cancellation token and cancellation source mirrors that separation between\n\u003e a promise and it's resolver.\n\u003e\n\u003e *Cancellation recovery is transient*  \n\u003e As a task progresses, the cleanup action may change. In the example above,\n\u003e if the data table requests more results upon scrolling, it's cancellation\n\u003e behavior when there's an outstanding query for more data is likely to be\n\u003e quite different than when it's got everything it needs displayed for the\n\u003e current page. That's the reason why the \"register\" method returns a\n\u003e capability to unregister the action.\n\n# TODO\n\nThe following is a high-level list of tasks to progress through each stage of the [TC39 proposal process](https://tc39.github.io/process-document/):\n\n### Stage 1 Entrance Criteria\n\n* [x] Identified a \"[champion][Champion]\" who will advance the addition.  \n* [x] [Prose][Prose] outlining the problem or need and the general shape of a solution.  \n* [x] Illustrative [examples][Examples] of usage.  \n* [x] High-level [API][API].  \n\n### Stage 2 Entrance Criteria\n\n* [ ] [Initial specification text][Specification].  \n* [ ] _Optional_. [Transpiler support][Transpiler] (for syntax) or [Polyfill][Polyfill] (for API).  \n\n### Stage 3 Entrance Criteria\n\n* [ ] [Complete specification text][Specification].  \n* [ ] Designated reviewers have [signed off][Stage3ReviewerSignOff] on the current spec text.  \n* [ ] The ECMAScript editor has [signed off][Stage3EditorSignOff] on the current spec text.  \n\n### Stage 4 Entrance Criteria\n\n* [ ] [Test262](https://github.com/tc39/test262) acceptance tests have been written for mainline usage scenarios and [merged][Test262PullRequest].  \n* [ ] Two compatible implementations which pass the acceptance tests: [\\[1\\]][Implementation1], [\\[2\\]][Implementation2].  \n* [ ] A [pull request][Ecma262PullRequest] has been sent to tc39/ecma262 with the integrated spec text.  \n* [ ] The ECMAScript editor has signed off on the [pull request][Ecma262PullRequest].  \n\n\u003c!-- The following are shared links used throughout the README: --\u003e\n\n[Object]: https://tc39.github.io/ecma262/#sec-object-constructor\n[String]: https://tc39.github.io/ecma262/#sec-string-constructor\n[Boolean]: https://tc39.github.io/ecma262/#sec-boolean-constructor\n[Function]: https://tc39.github.io/ecma262/#sec-function-constructor\n[Error]: https://tc39.github.io/ecma262/#sec-error-constructor\n[Iterable]: https://tc39.github.io/ecma262/#sec-symbol.iterator\n[JobQueue]: https://tc39.github.io/ecma262/#sec-jobs-and-job-queues\n[Champion]: #status\n[Prose]: #proposal\n[Examples]: #examples\n[Specification]: #todo\n[Transpiler]: #todo\n[Polyfill]: #todo\n[Stage3ReviewerSignOff]: #todo\n[Stage3EditorSignOff]: #todo\n[Test262PullRequest]: #todo\n[Implementation1]: #todo\n[Implementation2]: #todo\n[Ecma262PullRequest]: #todo\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-cancellation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftc39%2Fproposal-cancellation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftc39%2Fproposal-cancellation/lists"}