Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

feat(rxjs): fix #830, monkey patch rxjs to make rxjs run in correct zone #843

Merged
merged 18 commits into from
Jul 20, 2017

Conversation

JiaLiPassion
Copy link
Collaborator

@JiaLiPassion JiaLiPassion commented Jul 16, 2017

copy logic from @mhevery 's PR on rxjs repository, ReactiveX/rxjs#2266, try to monkey patch rxjs to make

  1. Observable construct
  2. Subscription
  3. Operator
  4. unsubscribe

run in correct zone.

now the test can pass in Firefox 54 and node.

@mhevery , please review the idea is workable or not.

currently the risky part is
https://github.com/angular/zone.js/pull/843/files#diff-7dba82bf33d9e5b20f59836ce1601b82R25
To patch Observable constructor.

const Observable = rxjs.Observable;

rxjs.Observable = function() {
  Observable.apply(this, arguments);
  this._zone = Zone.current;
  return this;
};

I am not sure patch like this is ok or not. I will continue to find some other way to make sure new Observable() will use this patched version.

If the idea is ok, I will add other test cases and make it work for other browsers.
Thank you!

lib/rxjs/rxjs.ts Outdated
return tearDownLogic.apply(this, arguments);
}
};
return patchedTeadDownLogic;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in 'patchedTeadDownLogic'.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mleibman, thanks!

gulpfile.js Outdated
@@ -199,6 +199,14 @@ gulp.task('build/sync-test.js', ['compile-esm'], function(cb) {
return generateScript('./lib/zone-spec/sync-test.ts', 'sync-test.js', false, cb);
});

gulp.task('build/rxjs.js', ['compile-esm'], function(cb) {
return generateScript('./lib/rxjs/rxjs.ts', 'zone-rxjs.js', false, cb);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should call it zone-patch-rxjs to be consistent with zone-patch-cordova?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, I will change it.

lib/rxjs/rxjs.ts Outdated
const unsubscribeSource = 'rxjs.Subscriber.unsubscribe';

try {
rxjs = require('rxjs');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

require statement will not work in browser.

So far the .js files have been stand alone code which can execute in global context, this means that we can't rely on ES6 import to get a hold of Rx.

There is also the matter that we have to run the code at a specific time. We have to load Zone.js first than Rx and only than we can do Rx patching but before the other code gets it.

I think the solution here is to package this file as umd that way it can be used in both cases.
It would mean that at some point one would have to import it as: zone.js/rxjs-patch

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it, I will try to organize the file into umd way.

lib/rxjs/rxjs.ts Outdated
const _subscribe = this._subscribe;
if (_zone) {
this._subscribe = function() {
const subscriber = arguments.length > 0 ? arguments[0] : undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

referring to arguments is slower. Can we find another way to do this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I will use an array to get the object.

lib/rxjs/rxjs.ts Outdated
if (this.operator && _zone && _zone !== currentZone) {
const call = this.operator.call;
this.operator.call = function() {
const subscriber = arguments.length > 0 ? arguments[0] : undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't refer to arguments

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I will use an array to get the object.

@JiaLiPassion
Copy link
Collaborator Author

JiaLiPassion commented Jul 19, 2017

@mhevery , I have modified the code based on your comment, and the travis build will be passed after #844 was merged, please review.

And I found current patch only handle the basic methods and operators of Observable (such as subscribe, lift), and the other methods and operators which are not included in Observable are not patched, such as Observable.bindCallback, The reason current patch not work is, for example, bindCallback's implementation like this,

// BoundCallbackObservable
static create<T>(func: Function,
                   selector: Function | void = undefined,
                   scheduler?: IScheduler): (...args: any[]) => Observable<T> {
    return function(this: any, ...args: any[]): Observable<T> {
      return new BoundCallbackObservable<T>(func, <any>selector, args, this, scheduler);
    };
  }

Observable.bindCallback =  BoundCallbackObservable.create

And BoundCallbackObservable constructor look like this,

// BoundCallbackObservable extends Observable
constructor(private callbackFunc: Function,
              private selector: Function,
              private args: any[],
              private context: any,
              private scheduler: IScheduler) {
    super();
  }

And the super is unpatched Observable, so the ConstructorZone is not captured.

I just added a new function and test cases to also patch the case like this, and I will continue to
patch other methods and operators which also inherit the Observable class.

Finally, all Observable creation operation should be patched.

Copy link
Contributor

@mhevery mhevery left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think fixing the import/UMD is the only thing which is left.

* found in the LICENSE file at https://angular.io/license
*/

let rxjs: any;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we do proper import {Rx} from 'rxjs/Rx';? I think that should work.

Copy link
Collaborator Author

@JiaLiPassion JiaLiPassion Jul 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a way to make import work in both

  • npm run test (local build env)
  • travis karma jasmine (dist env)

So we can use import * as Rx from 'rxjs/Rx' in rxjs.ts and rxjs.spec.ts now, please review.

lib/rxjs/rxjs.ts Outdated
*/

declare let define: any;
(function(root: any, factory: (Rx: any) => any) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

@JiaLiPassion JiaLiPassion Jul 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it into import * as Rx from 'rxjs/Rx'

lib/rxjs/rxjs.ts Outdated
(Rx: any) => {
'use strict';
Zone.__load_patch('rxjs', (global: any, Zone: ZoneType, api: _ZonePrivate) => {
const subscribeSource = 'rxjs.subscribe';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of these consts only nextSource seems to be used.

Copy link
Collaborator Author

@JiaLiPassion JiaLiPassion Jul 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have modified this one.

@JiaLiPassion
Copy link
Collaborator Author

JiaLiPassion commented Jul 20, 2017

@misko, I just updated the import/UMD part.

  • update test/main.ts, if we run in local build env, we just let systemjs load Rx.js for us.

  • add a global-rxjs.ts, if we run in travis dist env, we will load dist/zone-patch-rxjs.js which is an umd module, and will patch window.Rx, so we can't let systemjs to load Rx.js otherwise it will load another copy, so I add global-rxjs.ts to let systemjs load the object from window.Rx.

  • Now the karma test will work in both local build and travis dist env.

  • zone-patch-rxjs.js is built by rollup as an umd module.

Please review.

@misko
Copy link

misko commented Jul 20, 2017

@mhevery

@googlebot
Copy link

So there's good news and bad news.

👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there.

😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that they're okay with their commits being contributed to this project. Please have them confirm that here in the pull request.

Note to project maintainer: This is a terminal state, meaning the cla/google commit status will not change from this state. It's up to you to confirm consent of the commit author(s) and merge this pull request when appropriate.

1 similar comment
@googlebot
Copy link

So there's good news and bad news.

👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there.

😕 The bad news is that it appears that one or more commits were authored by someone other than the pull request submitter. We need to confirm that they're okay with their commits being contributed to this project. Please have them confirm that here in the pull request.

Note to project maintainer: This is a terminal state, meaning the cla/google commit status will not change from this state. It's up to you to confirm consent of the commit author(s) and merge this pull request when appropriate.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants