cancel.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. "use strict";
  2. module.exports = function(Promise, PromiseArray, apiRejection, debug) {
  3. var util = require("./util");
  4. var tryCatch = util.tryCatch;
  5. var errorObj = util.errorObj;
  6. var async = Promise._async;
  7. Promise.prototype["break"] = Promise.prototype.cancel = function() {
  8. if (!debug.cancellation()) return this._warn("cancellation is disabled");
  9. var promise = this;
  10. var child = promise;
  11. while (promise._isCancellable()) {
  12. if (!promise._cancelBy(child)) {
  13. if (child._isFollowing()) {
  14. child._followee().cancel();
  15. } else {
  16. child._cancelBranched();
  17. }
  18. break;
  19. }
  20. var parent = promise._cancellationParent;
  21. if (parent == null || !parent._isCancellable()) {
  22. if (promise._isFollowing()) {
  23. promise._followee().cancel();
  24. } else {
  25. promise._cancelBranched();
  26. }
  27. break;
  28. } else {
  29. if (promise._isFollowing()) promise._followee().cancel();
  30. promise._setWillBeCancelled();
  31. child = promise;
  32. promise = parent;
  33. }
  34. }
  35. };
  36. Promise.prototype._branchHasCancelled = function() {
  37. this._branchesRemainingToCancel--;
  38. };
  39. Promise.prototype._enoughBranchesHaveCancelled = function() {
  40. return this._branchesRemainingToCancel === undefined ||
  41. this._branchesRemainingToCancel <= 0;
  42. };
  43. Promise.prototype._cancelBy = function(canceller) {
  44. if (canceller === this) {
  45. this._branchesRemainingToCancel = 0;
  46. this._invokeOnCancel();
  47. return true;
  48. } else {
  49. this._branchHasCancelled();
  50. if (this._enoughBranchesHaveCancelled()) {
  51. this._invokeOnCancel();
  52. return true;
  53. }
  54. }
  55. return false;
  56. };
  57. Promise.prototype._cancelBranched = function() {
  58. if (this._enoughBranchesHaveCancelled()) {
  59. this._cancel();
  60. }
  61. };
  62. Promise.prototype._cancel = function() {
  63. if (!this._isCancellable()) return;
  64. this._setCancelled();
  65. async.invoke(this._cancelPromises, this, undefined);
  66. };
  67. Promise.prototype._cancelPromises = function() {
  68. if (this._length() > 0) this._settlePromises();
  69. };
  70. Promise.prototype._unsetOnCancel = function() {
  71. this._onCancelField = undefined;
  72. };
  73. Promise.prototype._isCancellable = function() {
  74. return this.isPending() && !this._isCancelled();
  75. };
  76. Promise.prototype.isCancellable = function() {
  77. return this.isPending() && !this.isCancelled();
  78. };
  79. Promise.prototype._doInvokeOnCancel = function(onCancelCallback, internalOnly) {
  80. if (util.isArray(onCancelCallback)) {
  81. for (var i = 0; i < onCancelCallback.length; ++i) {
  82. this._doInvokeOnCancel(onCancelCallback[i], internalOnly);
  83. }
  84. } else if (onCancelCallback !== undefined) {
  85. if (typeof onCancelCallback === "function") {
  86. if (!internalOnly) {
  87. var e = tryCatch(onCancelCallback).call(this._boundValue());
  88. if (e === errorObj) {
  89. this._attachExtraTrace(e.e);
  90. async.throwLater(e.e);
  91. }
  92. }
  93. } else {
  94. onCancelCallback._resultCancelled(this);
  95. }
  96. }
  97. };
  98. Promise.prototype._invokeOnCancel = function() {
  99. var onCancelCallback = this._onCancel();
  100. this._unsetOnCancel();
  101. async.invoke(this._doInvokeOnCancel, this, onCancelCallback);
  102. };
  103. Promise.prototype._invokeInternalOnCancel = function() {
  104. if (this._isCancellable()) {
  105. this._doInvokeOnCancel(this._onCancel(), true);
  106. this._unsetOnCancel();
  107. }
  108. };
  109. Promise.prototype._resultCancelled = function() {
  110. this.cancel();
  111. };
  112. };