finally.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. "use strict";
  2. module.exports = function(Promise, tryConvertToPromise, NEXT_FILTER) {
  3. var util = require("./util");
  4. var CancellationError = Promise.CancellationError;
  5. var errorObj = util.errorObj;
  6. var catchFilter = require("./catch_filter")(NEXT_FILTER);
  7. function PassThroughHandlerContext(promise, type, handler) {
  8. this.promise = promise;
  9. this.type = type;
  10. this.handler = handler;
  11. this.called = false;
  12. this.cancelPromise = null;
  13. }
  14. PassThroughHandlerContext.prototype.isFinallyHandler = function() {
  15. return this.type === 0;
  16. };
  17. function FinallyHandlerCancelReaction(finallyHandler) {
  18. this.finallyHandler = finallyHandler;
  19. }
  20. FinallyHandlerCancelReaction.prototype._resultCancelled = function() {
  21. checkCancel(this.finallyHandler);
  22. };
  23. function checkCancel(ctx, reason) {
  24. if (ctx.cancelPromise != null) {
  25. if (arguments.length > 1) {
  26. ctx.cancelPromise._reject(reason);
  27. } else {
  28. ctx.cancelPromise._cancel();
  29. }
  30. ctx.cancelPromise = null;
  31. return true;
  32. }
  33. return false;
  34. }
  35. function succeed() {
  36. return finallyHandler.call(this, this.promise._target()._settledValue());
  37. }
  38. function fail(reason) {
  39. if (checkCancel(this, reason)) return;
  40. errorObj.e = reason;
  41. return errorObj;
  42. }
  43. function finallyHandler(reasonOrValue) {
  44. var promise = this.promise;
  45. var handler = this.handler;
  46. if (!this.called) {
  47. this.called = true;
  48. var ret = this.isFinallyHandler()
  49. ? handler.call(promise._boundValue())
  50. : handler.call(promise._boundValue(), reasonOrValue);
  51. if (ret === NEXT_FILTER) {
  52. return ret;
  53. } else if (ret !== undefined) {
  54. promise._setReturnedNonUndefined();
  55. var maybePromise = tryConvertToPromise(ret, promise);
  56. if (maybePromise instanceof Promise) {
  57. if (this.cancelPromise != null) {
  58. if (maybePromise._isCancelled()) {
  59. var reason =
  60. new CancellationError("late cancellation observer");
  61. promise._attachExtraTrace(reason);
  62. errorObj.e = reason;
  63. return errorObj;
  64. } else if (maybePromise.isPending()) {
  65. maybePromise._attachCancellationCallback(
  66. new FinallyHandlerCancelReaction(this));
  67. }
  68. }
  69. return maybePromise._then(
  70. succeed, fail, undefined, this, undefined);
  71. }
  72. }
  73. }
  74. if (promise.isRejected()) {
  75. checkCancel(this);
  76. errorObj.e = reasonOrValue;
  77. return errorObj;
  78. } else {
  79. checkCancel(this);
  80. return reasonOrValue;
  81. }
  82. }
  83. Promise.prototype._passThrough = function(handler, type, success, fail) {
  84. if (typeof handler !== "function") return this.then();
  85. return this._then(success,
  86. fail,
  87. undefined,
  88. new PassThroughHandlerContext(this, type, handler),
  89. undefined);
  90. };
  91. Promise.prototype.lastly =
  92. Promise.prototype["finally"] = function (handler) {
  93. return this._passThrough(handler,
  94. 0,
  95. finallyHandler,
  96. finallyHandler);
  97. };
  98. Promise.prototype.tap = function (handler) {
  99. return this._passThrough(handler, 1, finallyHandler);
  100. };
  101. Promise.prototype.tapCatch = function (handlerOrPredicate) {
  102. var len = arguments.length;
  103. if(len === 1) {
  104. return this._passThrough(handlerOrPredicate,
  105. 1,
  106. undefined,
  107. finallyHandler);
  108. } else {
  109. var catchInstances = new Array(len - 1),
  110. j = 0, i;
  111. for (i = 0; i < len - 1; ++i) {
  112. var item = arguments[i];
  113. if (util.isObject(item)) {
  114. catchInstances[j++] = item;
  115. } else {
  116. return Promise.reject(new TypeError(
  117. "tapCatch statement predicate: "
  118. + "expecting an object but got " + util.classString(item)
  119. ));
  120. }
  121. }
  122. catchInstances.length = j;
  123. var handler = arguments[i];
  124. return this._passThrough(catchFilter(catchInstances, handler, this),
  125. 1,
  126. undefined,
  127. finallyHandler);
  128. }
  129. };
  130. return PassThroughHandlerContext;
  131. };