flow.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /** @license MIT License (c) copyright 2010-2014 original author or authors */
  2. /** @author Brian Cavalier */
  3. /** @author John Hann */
  4. (function(define) { 'use strict';
  5. define(function() {
  6. return function flow(Promise) {
  7. var resolve = Promise.resolve;
  8. var reject = Promise.reject;
  9. var origCatch = Promise.prototype['catch'];
  10. /**
  11. * Handle the ultimate fulfillment value or rejection reason, and assume
  12. * responsibility for all errors. If an error propagates out of result
  13. * or handleFatalError, it will be rethrown to the host, resulting in a
  14. * loud stack track on most platforms and a crash on some.
  15. * @param {function?} onResult
  16. * @param {function?} onError
  17. * @returns {undefined}
  18. */
  19. Promise.prototype.done = function(onResult, onError) {
  20. this._handler.visit(this._handler.receiver, onResult, onError);
  21. };
  22. /**
  23. * Add Error-type and predicate matching to catch. Examples:
  24. * promise.catch(TypeError, handleTypeError)
  25. * .catch(predicate, handleMatchedErrors)
  26. * .catch(handleRemainingErrors)
  27. * @param onRejected
  28. * @returns {*}
  29. */
  30. Promise.prototype['catch'] = Promise.prototype.otherwise = function(onRejected) {
  31. if (arguments.length < 2) {
  32. return origCatch.call(this, onRejected);
  33. }
  34. if(typeof onRejected !== 'function') {
  35. return this.ensure(rejectInvalidPredicate);
  36. }
  37. return origCatch.call(this, createCatchFilter(arguments[1], onRejected));
  38. };
  39. /**
  40. * Wraps the provided catch handler, so that it will only be called
  41. * if the predicate evaluates truthy
  42. * @param {?function} handler
  43. * @param {function} predicate
  44. * @returns {function} conditional catch handler
  45. */
  46. function createCatchFilter(handler, predicate) {
  47. return function(e) {
  48. return evaluatePredicate(e, predicate)
  49. ? handler.call(this, e)
  50. : reject(e);
  51. };
  52. }
  53. /**
  54. * Ensures that onFulfilledOrRejected will be called regardless of whether
  55. * this promise is fulfilled or rejected. onFulfilledOrRejected WILL NOT
  56. * receive the promises' value or reason. Any returned value will be disregarded.
  57. * onFulfilledOrRejected may throw or return a rejected promise to signal
  58. * an additional error.
  59. * @param {function} handler handler to be called regardless of
  60. * fulfillment or rejection
  61. * @returns {Promise}
  62. */
  63. Promise.prototype['finally'] = Promise.prototype.ensure = function(handler) {
  64. if(typeof handler !== 'function') {
  65. return this;
  66. }
  67. return this.then(function(x) {
  68. return runSideEffect(handler, this, identity, x);
  69. }, function(e) {
  70. return runSideEffect(handler, this, reject, e);
  71. });
  72. };
  73. function runSideEffect (handler, thisArg, propagate, value) {
  74. var result = handler.call(thisArg);
  75. return maybeThenable(result)
  76. ? propagateValue(result, propagate, value)
  77. : propagate(value);
  78. }
  79. function propagateValue (result, propagate, x) {
  80. return resolve(result).then(function () {
  81. return propagate(x);
  82. });
  83. }
  84. /**
  85. * Recover from a failure by returning a defaultValue. If defaultValue
  86. * is a promise, it's fulfillment value will be used. If defaultValue is
  87. * a promise that rejects, the returned promise will reject with the
  88. * same reason.
  89. * @param {*} defaultValue
  90. * @returns {Promise} new promise
  91. */
  92. Promise.prototype['else'] = Promise.prototype.orElse = function(defaultValue) {
  93. return this.then(void 0, function() {
  94. return defaultValue;
  95. });
  96. };
  97. /**
  98. * Shortcut for .then(function() { return value; })
  99. * @param {*} value
  100. * @return {Promise} a promise that:
  101. * - is fulfilled if value is not a promise, or
  102. * - if value is a promise, will fulfill with its value, or reject
  103. * with its reason.
  104. */
  105. Promise.prototype['yield'] = function(value) {
  106. return this.then(function() {
  107. return value;
  108. });
  109. };
  110. /**
  111. * Runs a side effect when this promise fulfills, without changing the
  112. * fulfillment value.
  113. * @param {function} onFulfilledSideEffect
  114. * @returns {Promise}
  115. */
  116. Promise.prototype.tap = function(onFulfilledSideEffect) {
  117. return this.then(onFulfilledSideEffect)['yield'](this);
  118. };
  119. return Promise;
  120. };
  121. function rejectInvalidPredicate() {
  122. throw new TypeError('catch predicate must be a function');
  123. }
  124. function evaluatePredicate(e, predicate) {
  125. return isError(predicate) ? e instanceof predicate : predicate(e);
  126. }
  127. function isError(predicate) {
  128. return predicate === Error
  129. || (predicate != null && predicate.prototype instanceof Error);
  130. }
  131. function maybeThenable(x) {
  132. return (typeof x === 'object' || typeof x === 'function') && x !== null;
  133. }
  134. function identity(x) {
  135. return x;
  136. }
  137. });
  138. }(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));