CancelToken.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. 'use strict';
  2. import CanceledError from './CanceledError.js';
  3. /**
  4. * A `CancelToken` is an object that can be used to request cancellation of an operation.
  5. *
  6. * @param {Function} executor The executor function.
  7. *
  8. * @returns {CancelToken}
  9. */
  10. class CancelToken {
  11. constructor(executor) {
  12. if (typeof executor !== 'function') {
  13. throw new TypeError('executor must be a function.');
  14. }
  15. let resolvePromise;
  16. this.promise = new Promise(function promiseExecutor(resolve) {
  17. resolvePromise = resolve;
  18. });
  19. const token = this;
  20. // eslint-disable-next-line func-names
  21. this.promise.then(cancel => {
  22. if (!token._listeners) return;
  23. let i = token._listeners.length;
  24. while (i-- > 0) {
  25. token._listeners[i](cancel);
  26. }
  27. token._listeners = null;
  28. });
  29. // eslint-disable-next-line func-names
  30. this.promise.then = onfulfilled => {
  31. let _resolve;
  32. // eslint-disable-next-line func-names
  33. const promise = new Promise(resolve => {
  34. token.subscribe(resolve);
  35. _resolve = resolve;
  36. }).then(onfulfilled);
  37. promise.cancel = function reject() {
  38. token.unsubscribe(_resolve);
  39. };
  40. return promise;
  41. };
  42. executor(function cancel(message, config, request) {
  43. if (token.reason) {
  44. // Cancellation has already been requested
  45. return;
  46. }
  47. token.reason = new CanceledError(message, config, request);
  48. resolvePromise(token.reason);
  49. });
  50. }
  51. /**
  52. * Throws a `CanceledError` if cancellation has been requested.
  53. */
  54. throwIfRequested() {
  55. if (this.reason) {
  56. throw this.reason;
  57. }
  58. }
  59. /**
  60. * Subscribe to the cancel signal
  61. */
  62. subscribe(listener) {
  63. if (this.reason) {
  64. listener(this.reason);
  65. return;
  66. }
  67. if (this._listeners) {
  68. this._listeners.push(listener);
  69. } else {
  70. this._listeners = [listener];
  71. }
  72. }
  73. /**
  74. * Unsubscribe from the cancel signal
  75. */
  76. unsubscribe(listener) {
  77. if (!this._listeners) {
  78. return;
  79. }
  80. const index = this._listeners.indexOf(listener);
  81. if (index !== -1) {
  82. this._listeners.splice(index, 1);
  83. }
  84. }
  85. toAbortSignal() {
  86. const controller = new AbortController();
  87. const abort = (err) => {
  88. controller.abort(err);
  89. };
  90. this.subscribe(abort);
  91. controller.signal.unsubscribe = () => this.unsubscribe(abort);
  92. return controller.signal;
  93. }
  94. /**
  95. * Returns an object that contains a new `CancelToken` and a function that, when called,
  96. * cancels the `CancelToken`.
  97. */
  98. static source() {
  99. let cancel;
  100. const token = new CancelToken(function executor(c) {
  101. cancel = c;
  102. });
  103. return {
  104. token,
  105. cancel
  106. };
  107. }
  108. }
  109. export default CancelToken;