Skip to content

Commit 14ffce6

Browse files
committed
feat(forEach): add thisArg
- adds thisArg as second argument to have symmetry with native Array forEach - eliminates closures by leveraging named function instances - adds test for thisArg passing - updates tests to accommodate thisArg BREAKING CHANGE: Observable.prototype.forEach argument order changed to accommodate thisArg. Optional PromiseCtor argument moved to third arg from second closes #878
1 parent d8f7297 commit 14ffce6

File tree

2 files changed

+38
-7
lines changed

2 files changed

+38
-7
lines changed

spec/observable-spec.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('Observable', function () {
2727
var expected = [1,2,3];
2828
var result = Observable.of(1,2,3).forEach(function (x) {
2929
expect(x).toBe(expected.shift());
30-
}, Promise)
30+
}, null, Promise)
3131
.then(done);
3232

3333
expect(typeof result.then).toBe('function');
@@ -41,7 +41,7 @@ describe('Observable', function () {
4141
}, function (err) {
4242
expect(err).toBe('bad');
4343
done();
44-
}, Promise);
44+
}, null, Promise);
4545
});
4646

4747
it('should allow Promise to be globally configured', function (done) {
@@ -60,6 +60,18 @@ describe('Observable', function () {
6060
done();
6161
});
6262
});
63+
64+
it('should accept a thisArg argument', function (done) {
65+
var expected = [1,2,3];
66+
var thisArg = {};
67+
var result = Observable.of(1,2,3).forEach(function (x) {
68+
expect(this).toBe(thisArg);
69+
expect(x).toBe(expected.shift());
70+
}, thisArg, Promise)
71+
.then(done);
72+
73+
expect(typeof result.then).toBe('function');
74+
});
6375
});
6476

6577
describe('subscribe', function () {

src/Observable.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,12 @@ export class Observable<T> implements CoreOperators<T> {
110110
/**
111111
* @method forEach
112112
* @param {Function} next a handler for each value emitted by the observable
113-
* @param {PromiseConstructor} PromiseCtor? a constructor function used to instantiate the Promise
113+
* @param {any} [thisArg] a `this` context for the `next` handler function
114+
* @param {PromiseConstructor} [PromiseCtor] a constructor function used to instantiate the Promise
114115
* @returns {Promise} a promise that either resolves on observable completion or
115116
* rejects with the handled error
116117
*/
117-
forEach(next: (value: T) => void, PromiseCtor?: PromiseConstructor): Promise<void> {
118+
forEach(next: (value: T) => void, thisArg: any, PromiseCtor?: PromiseConstructor): Promise<void> {
118119
if (!PromiseCtor) {
119120
if (root.Rx && root.Rx.config && root.Rx.config.Promise) {
120121
PromiseCtor = root.Rx.config.Promise;
@@ -127,9 +128,27 @@ export class Observable<T> implements CoreOperators<T> {
127128
throw new Error('no Promise impl found');
128129
}
129130

130-
return new PromiseCtor<void>((resolve, reject) => {
131-
this.subscribe(next, reject, resolve);
132-
});
131+
let nextHandler;
132+
133+
if (thisArg) {
134+
nextHandler = function nextHandlerFn(value: any): void {
135+
const { thisArg, next } = <any>nextHandlerFn;
136+
return next.call(thisArg, value);
137+
};
138+
nextHandler.thisArg = thisArg;
139+
nextHandler.next = next;
140+
} else {
141+
nextHandler = next;
142+
}
143+
144+
const promiseCallback = function promiseCallbackFn(resolve, reject) {
145+
const { source, nextHandler } = <any>promiseCallbackFn;
146+
source.subscribe(nextHandler, reject, resolve);
147+
};
148+
(<any>promiseCallback).source = this;
149+
(<any>promiseCallback).nextHandler = nextHandler;
150+
151+
return new PromiseCtor<void>(promiseCallback);
133152
}
134153

135154
_subscribe(subscriber: Subscriber<any>): Subscription<T> | Function | void {

0 commit comments

Comments
 (0)