You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. 'use strict';
  2. class CancelError extends Error {
  3. constructor(reason) {
  4. super(reason || 'Promise was canceled');
  5. this.name = 'CancelError';
  6. }
  7. get isCanceled() {
  8. return true;
  9. }
  10. }
  11. class PCancelable {
  12. static fn(userFn) {
  13. return (...arguments_) => {
  14. return new PCancelable((resolve, reject, onCancel) => {
  15. arguments_.push(onCancel);
  16. // eslint-disable-next-line promise/prefer-await-to-then
  17. userFn(...arguments_).then(resolve, reject);
  18. });
  19. };
  20. }
  21. constructor(executor) {
  22. this._cancelHandlers = [];
  23. this._isPending = true;
  24. this._isCanceled = false;
  25. this._rejectOnCancel = true;
  26. this._promise = new Promise((resolve, reject) => {
  27. this._reject = reject;
  28. const onResolve = value => {
  29. if (!this._isCanceled || !onCancel.shouldReject) {
  30. this._isPending = false;
  31. resolve(value);
  32. }
  33. };
  34. const onReject = error => {
  35. this._isPending = false;
  36. reject(error);
  37. };
  38. const onCancel = handler => {
  39. if (!this._isPending) {
  40. throw new Error('The `onCancel` handler was attached after the promise settled.');
  41. }
  42. this._cancelHandlers.push(handler);
  43. };
  44. Object.defineProperties(onCancel, {
  45. shouldReject: {
  46. get: () => this._rejectOnCancel,
  47. set: boolean => {
  48. this._rejectOnCancel = boolean;
  49. }
  50. }
  51. });
  52. return executor(onResolve, onReject, onCancel);
  53. });
  54. }
  55. then(onFulfilled, onRejected) {
  56. // eslint-disable-next-line promise/prefer-await-to-then
  57. return this._promise.then(onFulfilled, onRejected);
  58. }
  59. catch(onRejected) {
  60. return this._promise.catch(onRejected);
  61. }
  62. finally(onFinally) {
  63. return this._promise.finally(onFinally);
  64. }
  65. cancel(reason) {
  66. if (!this._isPending || this._isCanceled) {
  67. return;
  68. }
  69. this._isCanceled = true;
  70. if (this._cancelHandlers.length > 0) {
  71. try {
  72. for (const handler of this._cancelHandlers) {
  73. handler();
  74. }
  75. } catch (error) {
  76. this._reject(error);
  77. return;
  78. }
  79. }
  80. if (this._rejectOnCancel) {
  81. this._reject(new CancelError(reason));
  82. }
  83. }
  84. get isCanceled() {
  85. return this._isCanceled;
  86. }
  87. }
  88. Object.setPrototypeOf(PCancelable.prototype, Promise.prototype);
  89. module.exports = PCancelable;
  90. module.exports.CancelError = CancelError;