rx.async.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. // Copyright (c) Microsoft, All rights reserved. See License.txt in the project root for license information.
  2. ;(function (factory) {
  3. var objectTypes = {
  4. 'function': true,
  5. 'object': true
  6. };
  7. function checkGlobal(value) {
  8. return (value && value.Object === Object) ? value : null;
  9. }
  10. var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType) ? exports : null;
  11. var freeModule = (objectTypes[typeof module] && module && !module.nodeType) ? module : null;
  12. var freeGlobal = checkGlobal(freeExports && freeModule && typeof global === 'object' && global);
  13. var freeSelf = checkGlobal(objectTypes[typeof self] && self);
  14. var freeWindow = checkGlobal(objectTypes[typeof window] && window);
  15. var moduleExports = (freeModule && freeModule.exports === freeExports) ? freeExports : null;
  16. var thisGlobal = checkGlobal(objectTypes[typeof this] && this);
  17. var root = freeGlobal || ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) || freeSelf || thisGlobal || Function('return this')();
  18. // Because of build optimizers
  19. if (typeof define === 'function' && define.amd) {
  20. define(['./rx.binding', 'exports'], function (Rx, exports) {
  21. root.Rx = factory(root, exports, Rx);
  22. return root.Rx;
  23. });
  24. } else if (typeof module === 'object' && module && module.exports === freeExports) {
  25. module.exports = factory(root, module.exports, require('./rx'));
  26. } else {
  27. root.Rx = factory(root, {}, root.Rx);
  28. }
  29. }.call(this, function (root, exp, Rx, undefined) {
  30. // Aliases
  31. var Observable = Rx.Observable,
  32. observableFromPromise = Observable.fromPromise,
  33. observableThrow = Observable.throwError,
  34. AnonymousObservable = Rx.AnonymousObservable,
  35. ObservableBase = Rx.ObservableBase,
  36. AsyncSubject = Rx.AsyncSubject,
  37. disposableCreate = Rx.Disposable.create,
  38. CompositeDisposable = Rx.CompositeDisposable,
  39. immediateScheduler = Rx.Scheduler.immediate,
  40. defaultScheduler = Rx.Scheduler['default'],
  41. inherits = Rx.internals.inherits,
  42. isScheduler = Rx.Scheduler.isScheduler,
  43. isPromise = Rx.helpers.isPromise,
  44. isFunction = Rx.helpers.isFunction,
  45. isIterable = Rx.helpers.isIterable,
  46. isArrayLike = Rx.helpers.isArrayLike;
  47. var errorObj = {e: {}};
  48. function tryCatcherGen(tryCatchTarget) {
  49. return function tryCatcher() {
  50. try {
  51. return tryCatchTarget.apply(this, arguments);
  52. } catch (e) {
  53. errorObj.e = e;
  54. return errorObj;
  55. }
  56. };
  57. }
  58. var tryCatch = Rx.internals.tryCatch = function tryCatch(fn) {
  59. if (!isFunction(fn)) { throw new TypeError('fn must be a function'); }
  60. return tryCatcherGen(fn);
  61. };
  62. function thrower(e) {
  63. throw e;
  64. }
  65. Observable.wrap = function (fn) {
  66. function createObservable() {
  67. return Observable.spawn.call(this, fn.apply(this, arguments));
  68. }
  69. createObservable.__generatorFunction__ = fn;
  70. return createObservable;
  71. };
  72. var spawn = Observable.spawn = function () {
  73. var gen = arguments[0], self = this, args = [];
  74. for (var i = 1, len = arguments.length; i < len; i++) { args.push(arguments[i]); }
  75. return new AnonymousObservable(function (o) {
  76. var g = new CompositeDisposable();
  77. if (isFunction(gen)) { gen = gen.apply(self, args); }
  78. if (!gen || !isFunction(gen.next)) {
  79. o.onNext(gen);
  80. return o.onCompleted();
  81. }
  82. function processGenerator(res) {
  83. var ret = tryCatch(gen.next).call(gen, res);
  84. if (ret === errorObj) { return o.onError(ret.e); }
  85. next(ret);
  86. }
  87. processGenerator();
  88. function onError(err) {
  89. var ret = tryCatch(gen.next).call(gen, err);
  90. if (ret === errorObj) { return o.onError(ret.e); }
  91. next(ret);
  92. }
  93. function next(ret) {
  94. if (ret.done) {
  95. o.onNext(ret.value);
  96. o.onCompleted();
  97. return;
  98. }
  99. var obs = toObservable.call(self, ret.value);
  100. var value = null;
  101. var hasValue = false;
  102. if (Observable.isObservable(obs)) {
  103. g.add(obs.subscribe(function(val) {
  104. hasValue = true;
  105. value = val;
  106. }, onError, function() {
  107. hasValue && processGenerator(value);
  108. }));
  109. } else {
  110. onError(new TypeError('type not supported'));
  111. }
  112. }
  113. return g;
  114. });
  115. };
  116. function toObservable(obj) {
  117. if (!obj) { return obj; }
  118. if (Observable.isObservable(obj)) { return obj; }
  119. if (isPromise(obj)) { return Observable.fromPromise(obj); }
  120. if (isGeneratorFunction(obj) || isGenerator(obj)) { return spawn.call(this, obj); }
  121. if (isFunction(obj)) { return thunkToObservable.call(this, obj); }
  122. if (isArrayLike(obj) || isIterable(obj)) { return arrayToObservable.call(this, obj); }
  123. if (isObject(obj)) {return objectToObservable.call(this, obj);}
  124. return obj;
  125. }
  126. function arrayToObservable (obj) {
  127. return Observable.from(obj).concatMap(function(o) {
  128. if(Observable.isObservable(o) || isObject(o)) {
  129. return toObservable.call(null, o);
  130. } else {
  131. return Rx.Observable.just(o);
  132. }
  133. }).toArray();
  134. }
  135. function objectToObservable (obj) {
  136. var results = new obj.constructor(), keys = Object.keys(obj), observables = [];
  137. for (var i = 0, len = keys.length; i < len; i++) {
  138. var key = keys[i];
  139. var observable = toObservable.call(this, obj[key]);
  140. if(observable && Observable.isObservable(observable)) {
  141. defer(observable, key);
  142. } else {
  143. results[key] = obj[key];
  144. }
  145. }
  146. return Observable.forkJoin.apply(Observable, observables).map(function() {
  147. return results;
  148. });
  149. function defer (observable, key) {
  150. results[key] = undefined;
  151. observables.push(observable.map(function (next) {
  152. results[key] = next;
  153. }));
  154. }
  155. }
  156. function thunkToObservable(fn) {
  157. var self = this;
  158. return new AnonymousObservable(function (o) {
  159. fn.call(self, function () {
  160. var err = arguments[0], res = arguments[1];
  161. if (err) { return o.onError(err); }
  162. if (arguments.length > 2) {
  163. var args = [];
  164. for (var i = 1, len = arguments.length; i < len; i++) { args.push(arguments[i]); }
  165. res = args;
  166. }
  167. o.onNext(res);
  168. o.onCompleted();
  169. });
  170. });
  171. }
  172. function isGenerator(obj) {
  173. return isFunction (obj.next) && isFunction (obj['throw']);
  174. }
  175. function isGeneratorFunction(obj) {
  176. var ctor = obj.constructor;
  177. if (!ctor) { return false; }
  178. if (ctor.name === 'GeneratorFunction' || ctor.displayName === 'GeneratorFunction') { return true; }
  179. return isGenerator(ctor.prototype);
  180. }
  181. function isObject(val) {
  182. return Object == val.constructor;
  183. }
  184. /**
  185. * Invokes the specified function asynchronously on the specified scheduler, surfacing the result through an observable sequence.
  186. *
  187. * @example
  188. * var res = Rx.Observable.start(function () { console.log('hello'); });
  189. * var res = Rx.Observable.start(function () { console.log('hello'); }, Rx.Scheduler.timeout);
  190. * var res = Rx.Observable.start(function () { this.log('hello'); }, Rx.Scheduler.timeout, console);
  191. *
  192. * @param {Function} func Function to run asynchronously.
  193. * @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
  194. * @param [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
  195. * @returns {Observable} An observable sequence exposing the function's result value, or an exception.
  196. *
  197. * Remarks
  198. * * The function is called immediately, not during the subscription of the resulting sequence.
  199. * * Multiple subscriptions to the resulting sequence can observe the function's result.
  200. */
  201. Observable.start = function (func, context, scheduler) {
  202. return observableToAsync(func, context, scheduler)();
  203. };
  204. /**
  205. * Converts the function into an asynchronous function. Each invocation of the resulting asynchronous function causes an invocation of the original synchronous function on the specified scheduler.
  206. * @param {Function} function Function to convert to an asynchronous function.
  207. * @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
  208. * @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
  209. * @returns {Function} Asynchronous function.
  210. */
  211. var observableToAsync = Observable.toAsync = function (func, context, scheduler) {
  212. isScheduler(scheduler) || (scheduler = defaultScheduler);
  213. return function () {
  214. var args = arguments,
  215. subject = new AsyncSubject();
  216. scheduler.schedule(null, function () {
  217. var result;
  218. try {
  219. result = func.apply(context, args);
  220. } catch (e) {
  221. subject.onError(e);
  222. return;
  223. }
  224. subject.onNext(result);
  225. subject.onCompleted();
  226. });
  227. return subject.asObservable();
  228. };
  229. };
  230. function createCbObservable(fn, ctx, selector, args) {
  231. var o = new AsyncSubject();
  232. args.push(createCbHandler(o, ctx, selector));
  233. fn.apply(ctx, args);
  234. return o.asObservable();
  235. }
  236. function createCbHandler(o, ctx, selector) {
  237. return function handler () {
  238. var len = arguments.length, results = new Array(len);
  239. for(var i = 0; i < len; i++) { results[i] = arguments[i]; }
  240. if (isFunction(selector)) {
  241. results = tryCatch(selector).apply(ctx, results);
  242. if (results === errorObj) { return o.onError(results.e); }
  243. o.onNext(results);
  244. } else {
  245. if (results.length <= 1) {
  246. o.onNext(results[0]);
  247. } else {
  248. o.onNext(results);
  249. }
  250. }
  251. o.onCompleted();
  252. };
  253. }
  254. /**
  255. * Converts a callback function to an observable sequence.
  256. *
  257. * @param {Function} fn Function with a callback as the last parameter to convert to an Observable sequence.
  258. * @param {Mixed} [ctx] The context for the func parameter to be executed. If not specified, defaults to undefined.
  259. * @param {Function} [selector] A selector which takes the arguments from the callback to produce a single item to yield on next.
  260. * @returns {Function} A function, when executed with the required parameters minus the callback, produces an Observable sequence with a single value of the arguments to the callback as an array.
  261. */
  262. Observable.fromCallback = function (fn, ctx, selector) {
  263. return function () {
  264. typeof ctx === 'undefined' && (ctx = this);
  265. var len = arguments.length, args = new Array(len)
  266. for(var i = 0; i < len; i++) { args[i] = arguments[i]; }
  267. return createCbObservable(fn, ctx, selector, args);
  268. };
  269. };
  270. function createNodeObservable(fn, ctx, selector, args) {
  271. var o = new AsyncSubject();
  272. args.push(createNodeHandler(o, ctx, selector));
  273. fn.apply(ctx, args);
  274. return o.asObservable();
  275. }
  276. function createNodeHandler(o, ctx, selector) {
  277. return function handler () {
  278. var err = arguments[0];
  279. if (err) { return o.onError(err); }
  280. var len = arguments.length, results = [];
  281. for(var i = 1; i < len; i++) { results[i - 1] = arguments[i]; }
  282. if (isFunction(selector)) {
  283. var results = tryCatch(selector).apply(ctx, results);
  284. if (results === errorObj) { return o.onError(results.e); }
  285. o.onNext(results);
  286. } else {
  287. if (results.length <= 1) {
  288. o.onNext(results[0]);
  289. } else {
  290. o.onNext(results);
  291. }
  292. }
  293. o.onCompleted();
  294. };
  295. }
  296. /**
  297. * Converts a Node.js callback style function to an observable sequence. This must be in function (err, ...) format.
  298. * @param {Function} fn The function to call
  299. * @param {Mixed} [ctx] The context for the func parameter to be executed. If not specified, defaults to undefined.
  300. * @param {Function} [selector] A selector which takes the arguments from the callback minus the error to produce a single item to yield on next.
  301. * @returns {Function} An async function which when applied, returns an observable sequence with the callback arguments as an array.
  302. */
  303. Observable.fromNodeCallback = function (fn, ctx, selector) {
  304. return function () {
  305. typeof ctx === 'undefined' && (ctx = this);
  306. var len = arguments.length, args = new Array(len);
  307. for(var i = 0; i < len; i++) { args[i] = arguments[i]; }
  308. return createNodeObservable(fn, ctx, selector, args);
  309. };
  310. };
  311. function isNodeList(el) {
  312. if (root.StaticNodeList) {
  313. // IE8 Specific
  314. // instanceof is slower than Object#toString, but Object#toString will not work as intended in IE8
  315. return el instanceof root.StaticNodeList || el instanceof root.NodeList;
  316. } else {
  317. return Object.prototype.toString.call(el) === '[object NodeList]';
  318. }
  319. }
  320. function ListenDisposable(e, n, fn) {
  321. this._e = e;
  322. this._n = n;
  323. this._fn = fn;
  324. this._e.addEventListener(this._n, this._fn, false);
  325. this.isDisposed = false;
  326. }
  327. ListenDisposable.prototype.dispose = function () {
  328. if (!this.isDisposed) {
  329. this._e.removeEventListener(this._n, this._fn, false);
  330. this.isDisposed = true;
  331. }
  332. };
  333. function createEventListener (el, eventName, handler) {
  334. var disposables = new CompositeDisposable();
  335. // Asume NodeList or HTMLCollection
  336. var elemToString = Object.prototype.toString.call(el);
  337. if (isNodeList(el) || elemToString === '[object HTMLCollection]') {
  338. for (var i = 0, len = el.length; i < len; i++) {
  339. disposables.add(createEventListener(el.item(i), eventName, handler));
  340. }
  341. } else if (el) {
  342. disposables.add(new ListenDisposable(el, eventName, handler));
  343. }
  344. return disposables;
  345. }
  346. /**
  347. * Configuration option to determine whether to use native events only
  348. */
  349. Rx.config.useNativeEvents = false;
  350. var EventObservable = (function(__super__) {
  351. inherits(EventObservable, __super__);
  352. function EventObservable(el, name, fn) {
  353. this._el = el;
  354. this._n = name;
  355. this._fn = fn;
  356. __super__.call(this);
  357. }
  358. function createHandler(o, fn) {
  359. return function handler () {
  360. var results = arguments[0];
  361. if (isFunction(fn)) {
  362. results = tryCatch(fn).apply(null, arguments);
  363. if (results === errorObj) { return o.onError(results.e); }
  364. }
  365. o.onNext(results);
  366. };
  367. }
  368. EventObservable.prototype.subscribeCore = function (o) {
  369. return createEventListener(
  370. this._el,
  371. this._n,
  372. createHandler(o, this._fn));
  373. };
  374. return EventObservable;
  375. }(ObservableBase));
  376. /**
  377. * Creates an observable sequence by adding an event listener to the matching DOMElement or each item in the NodeList.
  378. * @param {Object} element The DOMElement or NodeList to attach a listener.
  379. * @param {String} eventName The event name to attach the observable sequence.
  380. * @param {Function} [selector] A selector which takes the arguments from the event handler to produce a single item to yield on next.
  381. * @returns {Observable} An observable sequence of events from the specified element and the specified event.
  382. */
  383. Observable.fromEvent = function (element, eventName, selector) {
  384. // Node.js specific
  385. if (element.addListener) {
  386. return fromEventPattern(
  387. function (h) { element.addListener(eventName, h); },
  388. function (h) { element.removeListener(eventName, h); },
  389. selector);
  390. }
  391. // Use only if non-native events are allowed
  392. if (!Rx.config.useNativeEvents) {
  393. // Handles jq, Angular.js, Zepto, Marionette, Ember.js
  394. if (typeof element.on === 'function' && typeof element.off === 'function') {
  395. return fromEventPattern(
  396. function (h) { element.on(eventName, h); },
  397. function (h) { element.off(eventName, h); },
  398. selector);
  399. }
  400. }
  401. return new EventObservable(element, eventName, selector).publish().refCount();
  402. };
  403. var EventPatternObservable = (function(__super__) {
  404. inherits(EventPatternObservable, __super__);
  405. function EventPatternObservable(add, del, fn) {
  406. this._add = add;
  407. this._del = del;
  408. this._fn = fn;
  409. __super__.call(this);
  410. }
  411. function createHandler(o, fn) {
  412. return function handler () {
  413. var results = arguments[0];
  414. if (isFunction(fn)) {
  415. results = tryCatch(fn).apply(null, arguments);
  416. if (results === errorObj) { return o.onError(results.e); }
  417. }
  418. o.onNext(results);
  419. };
  420. }
  421. EventPatternObservable.prototype.subscribeCore = function (o) {
  422. var fn = createHandler(o, this._fn);
  423. var returnValue = this._add(fn);
  424. return new EventPatternDisposable(this._del, fn, returnValue);
  425. };
  426. function EventPatternDisposable(del, fn, ret) {
  427. this._del = del;
  428. this._fn = fn;
  429. this._ret = ret;
  430. this.isDisposed = false;
  431. }
  432. EventPatternDisposable.prototype.dispose = function () {
  433. if(!this.isDisposed) {
  434. isFunction(this._del) && this._del(this._fn, this._ret);
  435. this.isDisposed = true;
  436. }
  437. };
  438. return EventPatternObservable;
  439. }(ObservableBase));
  440. /**
  441. * Creates an observable sequence from an event emitter via an addHandler/removeHandler pair.
  442. * @param {Function} addHandler The function to add a handler to the emitter.
  443. * @param {Function} [removeHandler] The optional function to remove a handler from an emitter.
  444. * @param {Function} [selector] A selector which takes the arguments from the event handler to produce a single item to yield on next.
  445. * @returns {Observable} An observable sequence which wraps an event from an event emitter
  446. */
  447. var fromEventPattern = Observable.fromEventPattern = function (addHandler, removeHandler, selector) {
  448. return new EventPatternObservable(addHandler, removeHandler, selector).publish().refCount();
  449. };
  450. /**
  451. * Invokes the asynchronous function, surfacing the result through an observable sequence.
  452. * @param {Function} functionAsync Asynchronous function which returns a Promise to run.
  453. * @returns {Observable} An observable sequence exposing the function's result value, or an exception.
  454. */
  455. Observable.startAsync = function (functionAsync) {
  456. var promise = tryCatch(functionAsync)();
  457. if (promise === errorObj) { return observableThrow(promise.e); }
  458. return observableFromPromise(promise);
  459. };
  460. return Rx;
  461. }));