cancellation.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { Emitter, Event } from './event.js';
  6. const shortcutEvent = Object.freeze(function (callback, context) {
  7. const handle = setTimeout(callback.bind(context), 0);
  8. return { dispose() { clearTimeout(handle); } };
  9. });
  10. export var CancellationToken;
  11. (function (CancellationToken) {
  12. function isCancellationToken(thing) {
  13. if (thing === CancellationToken.None || thing === CancellationToken.Cancelled) {
  14. return true;
  15. }
  16. if (thing instanceof MutableToken) {
  17. return true;
  18. }
  19. if (!thing || typeof thing !== 'object') {
  20. return false;
  21. }
  22. return typeof thing.isCancellationRequested === 'boolean'
  23. && typeof thing.onCancellationRequested === 'function';
  24. }
  25. CancellationToken.isCancellationToken = isCancellationToken;
  26. CancellationToken.None = Object.freeze({
  27. isCancellationRequested: false,
  28. onCancellationRequested: Event.None
  29. });
  30. CancellationToken.Cancelled = Object.freeze({
  31. isCancellationRequested: true,
  32. onCancellationRequested: shortcutEvent
  33. });
  34. })(CancellationToken || (CancellationToken = {}));
  35. class MutableToken {
  36. constructor() {
  37. this._isCancelled = false;
  38. this._emitter = null;
  39. }
  40. cancel() {
  41. if (!this._isCancelled) {
  42. this._isCancelled = true;
  43. if (this._emitter) {
  44. this._emitter.fire(undefined);
  45. this.dispose();
  46. }
  47. }
  48. }
  49. get isCancellationRequested() {
  50. return this._isCancelled;
  51. }
  52. get onCancellationRequested() {
  53. if (this._isCancelled) {
  54. return shortcutEvent;
  55. }
  56. if (!this._emitter) {
  57. this._emitter = new Emitter();
  58. }
  59. return this._emitter.event;
  60. }
  61. dispose() {
  62. if (this._emitter) {
  63. this._emitter.dispose();
  64. this._emitter = null;
  65. }
  66. }
  67. }
  68. export class CancellationTokenSource {
  69. constructor(parent) {
  70. this._token = undefined;
  71. this._parentListener = undefined;
  72. this._parentListener = parent && parent.onCancellationRequested(this.cancel, this);
  73. }
  74. get token() {
  75. if (!this._token) {
  76. // be lazy and create the token only when
  77. // actually needed
  78. this._token = new MutableToken();
  79. }
  80. return this._token;
  81. }
  82. cancel() {
  83. if (!this._token) {
  84. // save an object by returning the default
  85. // cancelled token when cancellation happens
  86. // before someone asks for the token
  87. this._token = CancellationToken.Cancelled;
  88. }
  89. else if (this._token instanceof MutableToken) {
  90. // actually cancel
  91. this._token.cancel();
  92. }
  93. }
  94. dispose(cancel = false) {
  95. if (cancel) {
  96. this.cancel();
  97. }
  98. if (this._parentListener) {
  99. this._parentListener.dispose();
  100. }
  101. if (!this._token) {
  102. // ensure to initialize with an empty token if we had none
  103. this._token = CancellationToken.None;
  104. }
  105. else if (this._token instanceof MutableToken) {
  106. // actually dispose
  107. this._token.dispose();
  108. }
  109. }
  110. }