esnext.observable.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. 'use strict';
  2. // https://github.com/tc39/proposal-observable
  3. var $ = require('../internals/export');
  4. var DESCRIPTORS = require('../internals/descriptors');
  5. var setSpecies = require('../internals/set-species');
  6. var aFunction = require('../internals/a-function');
  7. var anObject = require('../internals/an-object');
  8. var isObject = require('../internals/is-object');
  9. var anInstance = require('../internals/an-instance');
  10. var defineProperty = require('../internals/object-define-property').f;
  11. var createNonEnumerableProperty = require('../internals/create-non-enumerable-property');
  12. var redefineAll = require('../internals/redefine-all');
  13. var getIterator = require('../internals/get-iterator');
  14. var iterate = require('../internals/iterate');
  15. var hostReportErrors = require('../internals/host-report-errors');
  16. var wellKnownSymbol = require('../internals/well-known-symbol');
  17. var InternalStateModule = require('../internals/internal-state');
  18. var OBSERVABLE = wellKnownSymbol('observable');
  19. var getInternalState = InternalStateModule.get;
  20. var setInternalState = InternalStateModule.set;
  21. var getMethod = function (fn) {
  22. return fn == null ? undefined : aFunction(fn);
  23. };
  24. var cleanupSubscription = function (subscriptionState) {
  25. var cleanup = subscriptionState.cleanup;
  26. if (cleanup) {
  27. subscriptionState.cleanup = undefined;
  28. try {
  29. cleanup();
  30. } catch (error) {
  31. hostReportErrors(error);
  32. }
  33. }
  34. };
  35. var subscriptionClosed = function (subscriptionState) {
  36. return subscriptionState.observer === undefined;
  37. };
  38. var close = function (subscription, subscriptionState) {
  39. if (!DESCRIPTORS) {
  40. subscription.closed = true;
  41. var subscriptionObserver = subscriptionState.subscriptionObserver;
  42. if (subscriptionObserver) subscriptionObserver.closed = true;
  43. } subscriptionState.observer = undefined;
  44. };
  45. var Subscription = function (observer, subscriber) {
  46. var subscriptionState = setInternalState(this, {
  47. cleanup: undefined,
  48. observer: anObject(observer),
  49. subscriptionObserver: undefined
  50. });
  51. var start;
  52. if (!DESCRIPTORS) this.closed = false;
  53. try {
  54. if (start = getMethod(observer.start)) start.call(observer, this);
  55. } catch (error) {
  56. hostReportErrors(error);
  57. }
  58. if (subscriptionClosed(subscriptionState)) return;
  59. var subscriptionObserver = subscriptionState.subscriptionObserver = new SubscriptionObserver(this);
  60. try {
  61. var cleanup = subscriber(subscriptionObserver);
  62. var subscription = cleanup;
  63. if (cleanup != null) subscriptionState.cleanup = typeof cleanup.unsubscribe === 'function'
  64. ? function () { subscription.unsubscribe(); }
  65. : aFunction(cleanup);
  66. } catch (error) {
  67. subscriptionObserver.error(error);
  68. return;
  69. } if (subscriptionClosed(subscriptionState)) cleanupSubscription(subscriptionState);
  70. };
  71. Subscription.prototype = redefineAll({}, {
  72. unsubscribe: function unsubscribe() {
  73. var subscriptionState = getInternalState(this);
  74. if (!subscriptionClosed(subscriptionState)) {
  75. close(this, subscriptionState);
  76. cleanupSubscription(subscriptionState);
  77. }
  78. }
  79. });
  80. if (DESCRIPTORS) defineProperty(Subscription.prototype, 'closed', {
  81. configurable: true,
  82. get: function () {
  83. return subscriptionClosed(getInternalState(this));
  84. }
  85. });
  86. var SubscriptionObserver = function (subscription) {
  87. setInternalState(this, { subscription: subscription });
  88. if (!DESCRIPTORS) this.closed = false;
  89. };
  90. SubscriptionObserver.prototype = redefineAll({}, {
  91. next: function next(value) {
  92. var subscriptionState = getInternalState(getInternalState(this).subscription);
  93. if (!subscriptionClosed(subscriptionState)) {
  94. var observer = subscriptionState.observer;
  95. try {
  96. var nextMethod = getMethod(observer.next);
  97. if (nextMethod) nextMethod.call(observer, value);
  98. } catch (error) {
  99. hostReportErrors(error);
  100. }
  101. }
  102. },
  103. error: function error(value) {
  104. var subscription = getInternalState(this).subscription;
  105. var subscriptionState = getInternalState(subscription);
  106. if (!subscriptionClosed(subscriptionState)) {
  107. var observer = subscriptionState.observer;
  108. close(subscription, subscriptionState);
  109. try {
  110. var errorMethod = getMethod(observer.error);
  111. if (errorMethod) errorMethod.call(observer, value);
  112. else hostReportErrors(value);
  113. } catch (err) {
  114. hostReportErrors(err);
  115. } cleanupSubscription(subscriptionState);
  116. }
  117. },
  118. complete: function complete() {
  119. var subscription = getInternalState(this).subscription;
  120. var subscriptionState = getInternalState(subscription);
  121. if (!subscriptionClosed(subscriptionState)) {
  122. var observer = subscriptionState.observer;
  123. close(subscription, subscriptionState);
  124. try {
  125. var completeMethod = getMethod(observer.complete);
  126. if (completeMethod) completeMethod.call(observer);
  127. } catch (error) {
  128. hostReportErrors(error);
  129. } cleanupSubscription(subscriptionState);
  130. }
  131. }
  132. });
  133. if (DESCRIPTORS) defineProperty(SubscriptionObserver.prototype, 'closed', {
  134. configurable: true,
  135. get: function () {
  136. return subscriptionClosed(getInternalState(getInternalState(this).subscription));
  137. }
  138. });
  139. var $Observable = function Observable(subscriber) {
  140. anInstance(this, $Observable, 'Observable');
  141. setInternalState(this, { subscriber: aFunction(subscriber) });
  142. };
  143. redefineAll($Observable.prototype, {
  144. subscribe: function subscribe(observer) {
  145. var length = arguments.length;
  146. return new Subscription(typeof observer === 'function' ? {
  147. next: observer,
  148. error: length > 1 ? arguments[1] : undefined,
  149. complete: length > 2 ? arguments[2] : undefined
  150. } : isObject(observer) ? observer : {}, getInternalState(this).subscriber);
  151. }
  152. });
  153. redefineAll($Observable, {
  154. from: function from(x) {
  155. var C = typeof this === 'function' ? this : $Observable;
  156. var observableMethod = getMethod(anObject(x)[OBSERVABLE]);
  157. if (observableMethod) {
  158. var observable = anObject(observableMethod.call(x));
  159. return observable.constructor === C ? observable : new C(function (observer) {
  160. return observable.subscribe(observer);
  161. });
  162. }
  163. var iterator = getIterator(x);
  164. return new C(function (observer) {
  165. iterate(iterator, function (it) {
  166. observer.next(it);
  167. if (observer.closed) return iterate.stop();
  168. }, undefined, false, true);
  169. observer.complete();
  170. });
  171. },
  172. of: function of() {
  173. var C = typeof this === 'function' ? this : $Observable;
  174. var length = arguments.length;
  175. var items = new Array(length);
  176. var index = 0;
  177. while (index < length) items[index] = arguments[index++];
  178. return new C(function (observer) {
  179. for (var i = 0; i < length; i++) {
  180. observer.next(items[i]);
  181. if (observer.closed) return;
  182. } observer.complete();
  183. });
  184. }
  185. });
  186. createNonEnumerableProperty($Observable.prototype, OBSERVABLE, function () { return this; });
  187. $({ global: true }, {
  188. Observable: $Observable
  189. });
  190. setSpecies('Observable');