From e7eb5d76d917075200e733f7e83f0dfbaead56ab Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 17 Sep 2015 14:20:59 -0700 Subject: [PATCH] feat(config): add global configuration of Promise capability closes #115 --- spec/helpers/test-helper.js | 28 ++++++++++++++++++++- spec/observable-spec.js | 43 +++++++++++++++++++++++++++++++- spec/operators/toPromise-spec.js | 22 +++++++++++++--- src/Observable.ts | 18 ++++++++++--- src/Rx.ts | 2 +- src/operators/toPromise.ts | 15 ++++++++++- 6 files changed, 118 insertions(+), 10 deletions(-) diff --git a/spec/helpers/test-helper.js b/spec/helpers/test-helper.js index 6ff5f95f43..042071531a 100644 --- a/spec/helpers/test-helper.js +++ b/spec/helpers/test-helper.js @@ -77,4 +77,30 @@ afterEach(function () { function assertDeepEqual(actual, expected) { expect(actual).toDeepEqual(expected); -} \ No newline at end of file +} + + +(function (){ + var objectTypes = { + 'boolean': false, + 'function': true, + 'object': true, + 'number': false, + 'string': false, + 'undefined': false + }; + + var _root = (objectTypes[typeof self] && self) || (objectTypes[typeof window] && window); + + var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; + var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; + var freeGlobal = objectTypes[typeof global] && global; + + if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) { + _root = freeGlobal; + } + + + + global.__root__ = _root; +}()); \ No newline at end of file diff --git a/spec/observable-spec.js b/spec/observable-spec.js index 2732257087..ac227c2491 100644 --- a/spec/observable-spec.js +++ b/spec/observable-spec.js @@ -1,6 +1,6 @@ /* globals describe, it, expect */ var Rx = require('../dist/cjs/Rx'); - +var Promise = require('promise'); var Observable = Rx.Observable; describe('Observable', function () { @@ -14,6 +14,47 @@ describe('Observable', function () { source.subscribe(function (x) { expect(x).toBe(1); }, null, done); }); + describe('forEach', function(){ + it('should iterate and return a Promise', function (done){ + var expected = [1,2,3]; + var result = Observable.of(1,2,3).forEach(function(x) { + expect(x).toBe(expected.shift()); + }, Promise) + .then(done); + + expect(typeof result.then).toBe('function'); + }); + + it('should reject promise when in error', function(done){ + Observable.throw('bad').forEach(function(x) { + throw 'should not be called'; + }).then(function() { + throw 'should not complete'; + }, function(err) { + expect(err).toBe('bad'); + done(); + }, Promise); + }); + + it('should allow Promise to be globally configured', function (done) { + var wasCalled = false; + __root__.Rx = {}; + __root__.Rx.config = {}; + __root__.Rx.config.Promise = function MyPromise(callback) { + wasCalled = true; + return new Promise(callback); + }; + + + Observable.of(42).forEach(function(x) { + expect(x).toBe(42); + }).then(function(){ + expect(wasCalled).toBe(true); + done(); + }); + }); + }); + describe('subscribe', function () { it('should be synchronous', function () { var subscribed = false; diff --git a/spec/operators/toPromise-spec.js b/spec/operators/toPromise-spec.js index fc8323503a..b83cd7fa57 100644 --- a/spec/operators/toPromise-spec.js +++ b/spec/operators/toPromise-spec.js @@ -1,22 +1,38 @@ /* globals describe, it, expect */ var Rx = require('../../dist/cjs/Rx'); -var promise = require('promise'); +var Promise = require('promise'); var Observable = Rx.Observable; describe('Observable.prototype.toPromise()', function () { it('should convert an Observable to a promise of its last value', function (done) { - Observable.of(1, 2, 3).toPromise(promise).then(function (x) { + Observable.of(1, 2, 3).toPromise(Promise).then(function (x) { expect(x).toBe(3); done(); }); }); it('should handle errors properly', function (done) { - Observable.throw('bad').toPromise(promise).then(function () { + Observable.throw('bad').toPromise(Promise).then(function () { throw 'should not be called'; }, function (err) { expect(err).toBe('bad'); done(); }); }); + + it('should allow for global config via Rx.config.Promise', function(done){ + var wasCalled = false; + __root__.Rx = {}; + __root__.Rx.config = {}; + __root__.Rx.config.Promise = function MyPromise(callback) { + wasCalled = true; + return new Promise(callback); + }; + + Observable.of(42).toPromise().then(function(x) { + expect(wasCalled).toBe(true); + expect(x).toBe(42); + done(); + }); + }); }); \ No newline at end of file diff --git a/src/Observable.ts b/src/Observable.ts index efa0cce0cc..d104872aa4 100644 --- a/src/Observable.ts +++ b/src/Observable.ts @@ -6,7 +6,7 @@ import Subscriber from './Subscriber'; import Subscription from './Subscription'; import ConnectableObservable from './observables/ConnectableObservable'; import GroupSubject from './subjects/GroupSubject'; - +import {root} from './util/root'; import $$observable from './util/Symbol_observable'; @@ -112,8 +112,20 @@ export default class Observable { * @returns {Promise} a promise that either resolves on observable completion or * rejects with the handled error */ - forEach(next:(value:T) => void) { - return new Promise((resolve, reject) => { + forEach(next:(value:T) => void, PromiseCtor?: PromiseConstructor): Promise { + if(!PromiseCtor) { + if(root.Rx && root.Rx.config && root.Rx.config.Promise) { + PromiseCtor = root.Rx.config.Promise; + } else if (root.Promise) { + PromiseCtor = root.Promise; + } + } + + if(!PromiseCtor) { + throw new Error('no Promise impl found'); + } + + return new PromiseCtor((resolve, reject) => { this.subscribe(next, reject, resolve); }); } diff --git a/src/Rx.ts b/src/Rx.ts index 1aeaf0aa75..864c6274dd 100644 --- a/src/Rx.ts +++ b/src/Rx.ts @@ -241,5 +241,5 @@ export { VirtualTimeScheduler, TestScheduler, EmptyError, - ArgumentOutOfRangeError + ArgumentOutOfRangeError, }; diff --git a/src/operators/toPromise.ts b/src/operators/toPromise.ts index b880a32326..40c63fb4ec 100644 --- a/src/operators/toPromise.ts +++ b/src/operators/toPromise.ts @@ -1,6 +1,19 @@ import Subscriber from '../Subscriber'; +import { root } from '../util/root'; -export default function toPromise(PromiseCtor: PromiseConstructor = Promise): Promise { +export default function toPromise(PromiseCtor?: PromiseConstructor): Promise { + if(!PromiseCtor) { + if(root.Rx && root.Rx.config && root.Rx.config.Promise) { + PromiseCtor = root.Rx.config.Promise; + } else if (root.Promise) { + PromiseCtor = root.Promise; + } + } + + if(!PromiseCtor) { + throw new Error('no Promise impl found'); + } + return new PromiseCtor((resolve, reject) => { let value: any; this.subscribe(x => value = x, err => reject(err), () => resolve(value));