X Tutup
Skip to content

Commit 491e1fd

Browse files
committed
feat: move NgZone to Stream/Observable-based callback API
BREAKING CHANGES: - deprecates these methods in NgZone: overrideOnTurnStart, overrideOnTurnDone, overrideOnEventDone, overrideOnErrorHandler - introduces new API in NgZone that may shadow other API used by existing applications.
1 parent a7c95ad commit 491e1fd

File tree

9 files changed

+1111
-119
lines changed

9 files changed

+1111
-119
lines changed

modules/angular2/src/core/facade/async.dart

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ class ObservableWrapper {
3535
return obs is Stream;
3636
}
3737

38+
/**
39+
* Returns whether `emitter` has any subscribers listening to events.
40+
*/
41+
static bool hasSubscribers(EventEmitter emitter) {
42+
return emitter._controller.hasListener;
43+
}
44+
3845
static void dispose(StreamSubscription s) {
3946
s.cancel();
4047
}
@@ -55,8 +62,10 @@ class ObservableWrapper {
5562
class EventEmitter extends Stream {
5663
StreamController<dynamic> _controller;
5764

58-
EventEmitter() {
59-
_controller = new StreamController.broadcast();
65+
/// Creates an instance of [EventEmitter], which depending on [isAsync],
66+
/// delivers events synchronously or asynchronously.
67+
EventEmitter([bool isAsync = true]) {
68+
_controller = new StreamController.broadcast(sync: !isAsync);
6069
}
6170

6271
StreamSubscription listen(void onData(dynamic line),

modules/angular2/src/core/facade/async.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ export class ObservableWrapper {
3232

3333
static isObservable(obs: any): boolean { return obs instanceof Observable; }
3434

35+
/**
36+
* Returns whether `obs` has any subscribers listening to events.
37+
*/
38+
static hasSubscribers(obs: EventEmitter): boolean { return obs._subject.observers.length > 0; }
39+
3540
static dispose(subscription: any) { subscription.unsubscribe(); }
3641

3742
static callNext(emitter: EventEmitter, value: any) { emitter.next(value); }
@@ -88,9 +93,22 @@ export class Observable {
8893
export class EventEmitter extends Observable {
8994
/** @internal */
9095
_subject = new Subject();
96+
/** @internal */
97+
_isAsync: boolean;
98+
99+
/**
100+
* Creates an instance of [EventEmitter], which depending on [isAsync],
101+
* delivers events synchronously or asynchronously.
102+
*/
103+
constructor(isAsync: boolean = true) {
104+
super();
105+
this._isAsync = isAsync;
106+
}
91107

92108
observer(generator: any): any {
93-
return this._subject.subscribe((value) => { setTimeout(() => generator.next(value)); },
109+
var schedulerFn = this._isAsync ? (value) => { setTimeout(() => generator.next(value)); } :
110+
(value) => { generator.next(value); };
111+
return this._subject.subscribe(schedulerFn,
94112
(error) => generator.throw ? generator.throw(error) : null,
95113
() => generator.return ? generator.return () : null);
96114
}

modules/angular2/src/core/zone.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
// Public API for Zone
2-
export {NgZone} from './zone/ng_zone';
2+
export {NgZone, ZeroArgFunction, ErrorHandlingFn, NgZoneError} from './zone/ng_zone';

modules/angular2/src/core/zone/ng_zone.dart

Lines changed: 108 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ class WrappedTimer implements Timer {
3535
bool get isActive => _timer.isActive;
3636
}
3737

38+
/**
39+
* Stores error information; delivered via [NgZone.onError] stream.
40+
*/
41+
class NgZoneError {
42+
/// Error object thrown.
43+
final error;
44+
/// Either long or short chain of stack traces.
45+
final List stackTrace;
46+
NgZoneError(this.error, this.stackTrace);
47+
}
48+
3849
/**
3950
* A `Zone` wrapper that lets you schedule tasks after its private microtask queue is exhausted but
4051
* before the next "VM turn", i.e. event loop iteration.
@@ -56,6 +67,12 @@ class NgZone {
5667
ZeroArgFunction _onEventDone;
5768
ErrorHandlingFn _onErrorHandler;
5869

70+
final _onTurnStartCtrl = new StreamController.broadcast(sync: true);
71+
final _onTurnDoneCtrl = new StreamController.broadcast(sync: true);
72+
final _onEventDoneCtrl = new StreamController.broadcast(sync: true);
73+
final _onErrorCtrl =
74+
new StreamController<NgZoneError>.broadcast(sync: true);
75+
5976
// Code executed in _mountZone does not trigger the onTurnDone.
6077
Zone _mountZone;
6178
// _innerZone is the child of _mountZone. Any code executed in this zone will trigger the
@@ -103,18 +120,43 @@ class NgZone {
103120
* Sets the zone hook that is called just before Angular event turn starts.
104121
* It is called once per browser event.
105122
*/
123+
@Deprecated('Use onTurnStart Stream instead')
106124
void overrideOnTurnStart(ZeroArgFunction onTurnStartFn) {
107125
_onTurnStart = onTurnStartFn;
108126
}
109127

128+
void _notifyOnTurnStart() {
129+
this._onTurnStartCtrl.add(null);
130+
}
131+
132+
/**
133+
* Notifies subscribers just before Angular event turn starts.
134+
*
135+
* Emits an event once per browser task that is handled by Angular.
136+
*/
137+
Stream get onTurnStart => _onTurnStartCtrl.stream;
138+
110139
/**
111140
* Sets the zone hook that is called immediately after Angular processes
112141
* all pending microtasks.
113142
*/
143+
@Deprecated('Use onTurnDone Stream instead')
114144
void overrideOnTurnDone(ZeroArgFunction onTurnDoneFn) {
115145
_onTurnDone = onTurnDoneFn;
116146
}
117147

148+
/**
149+
* Notifies subscribers immediately after the Angular zone is done processing
150+
* the current turn and any microtasks scheduled from that turn.
151+
*
152+
* Used by Angular as a signal to kick off change-detection.
153+
*/
154+
Stream get onTurnDone => _onTurnDoneCtrl.stream;
155+
156+
void _notifyOnTurnDone() {
157+
this._onTurnDoneCtrl.add(null);
158+
}
159+
118160
/**
119161
* Sets the zone hook that is called immediately after the last turn in
120162
* an event completes. At this point Angular will no longer attempt to
@@ -123,6 +165,7 @@ class NgZone {
123165
*
124166
* This hook is useful for validating application state (e.g. in a test).
125167
*/
168+
@Deprecated('Use onEventDone Stream instead')
126169
void overrideOnEventDone(ZeroArgFunction onEventDoneFn,
127170
[bool waitForAsync = false]) {
128171
_onEventDone = onEventDoneFn;
@@ -136,15 +179,55 @@ class NgZone {
136179
}
137180
}
138181

182+
/**
183+
* Notifies subscribers immediately after the final `onTurnDone` callback
184+
* before ending VM event.
185+
*
186+
* This event is useful for validating application state (e.g. in a test).
187+
*/
188+
Stream get onEventDone => _onEventDoneCtrl.stream;
189+
190+
void _notifyOnEventDone() {
191+
this._onEventDoneCtrl.add(null);
192+
}
193+
194+
/**
195+
* Whether there are any outstanding microtasks.
196+
*/
197+
bool get hasPendingMicrotasks => _pendingMicrotasks > 0;
198+
199+
/**
200+
* Whether there are any outstanding timers.
201+
*/
202+
bool get hasPendingTimers => _pendingTimers.isNotEmpty;
203+
204+
/**
205+
* Whether there are any outstanding asychnronous tasks of any kind that are
206+
* scheduled to run within Angular zone.
207+
*
208+
* Useful as a signal of UI stability. For example, when a test reaches a
209+
* point when [hasPendingAsyncTasks] is `false` it might be a good time to run
210+
* test expectations.
211+
*/
212+
bool get hasPendingAsyncTasks => hasPendingMicrotasks || hasPendingTimers;
213+
139214
/**
140215
* Sets the zone hook that is called when an error is uncaught in the
141216
* Angular zone. The first argument is the error. The second argument is
142217
* the stack trace.
143218
*/
219+
@Deprecated('Use onError Stream instead')
144220
void overrideOnErrorHandler(ErrorHandlingFn errorHandlingFn) {
145221
_onErrorHandler = errorHandlingFn;
146222
}
147223

224+
/**
225+
* Notifies subscribers whenever an error happens within the zone.
226+
*
227+
* Useful for logging.
228+
*/
229+
Stream get onError => _onErrorCtrl.stream;
230+
148231
/**
149232
* Runs `fn` in the inner zone and returns whatever it returns.
150233
*
@@ -194,6 +277,7 @@ class NgZone {
194277
void _maybeStartVmTurn(ZoneDelegate parent) {
195278
if (!_hasExecutedCodeInInnerZone) {
196279
_hasExecutedCodeInInnerZone = true;
280+
parent.run(_innerZone, _notifyOnTurnStart);
197281
if (_onTurnStart != null) {
198282
parent.run(_innerZone, _onTurnStart);
199283
}
@@ -209,19 +293,25 @@ class NgZone {
209293
_nestedRun--;
210294
// If there are no more pending microtasks and we are not in a recursive call, this is the end of a turn
211295
if (_pendingMicrotasks == 0 && _nestedRun == 0 && !_inVmTurnDone) {
212-
if (_onTurnDone != null && _hasExecutedCodeInInnerZone) {
296+
if (_hasExecutedCodeInInnerZone) {
213297
// Trigger onTurnDone at the end of a turn if _innerZone has executed some code
214298
try {
215299
_inVmTurnDone = true;
216-
parent.run(_innerZone, _onTurnDone);
300+
_notifyOnTurnDone();
301+
if (_onTurnDone != null) {
302+
parent.run(_innerZone, _onTurnDone);
303+
}
217304
} finally {
218305
_inVmTurnDone = false;
219306
_hasExecutedCodeInInnerZone = false;
220307
}
221308
}
222309

223-
if (_pendingMicrotasks == 0 && _onEventDone != null) {
224-
runOutsideAngular(_onEventDone);
310+
if (_pendingMicrotasks == 0) {
311+
_notifyOnEventDone();
312+
if (_onEventDone != null) {
313+
runOutsideAngular(_onEventDone);
314+
}
225315
}
226316
}
227317
}
@@ -248,18 +338,28 @@ class NgZone {
248338

249339
// Called by Chain.capture() on errors when long stack traces are enabled
250340
void _onErrorWithLongStackTrace(error, Chain chain) {
251-
if (_onErrorHandler != null) {
341+
if (_onErrorHandler != null || _onErrorCtrl.hasListener) {
252342
final traces = chain.terse.traces.map((t) => t.toString()).toList();
253-
_onErrorHandler(error, traces);
343+
if (_onErrorCtrl.hasListener) {
344+
_onErrorCtrl.add(new NgZoneError(error, traces));
345+
}
346+
if (_onErrorHandler != null) {
347+
_onErrorHandler(error, traces);
348+
}
254349
} else {
255350
throw error;
256351
}
257352
}
258353

259354
// Outer zone handleUnchaughtError when long stack traces are not used
260355
void _onErrorWithoutLongStackTrace(error, StackTrace trace) {
261-
if (_onErrorHandler != null) {
262-
_onErrorHandler(error, [trace.toString()]);
356+
if (_onErrorHandler != null || _onErrorCtrl.hasListener) {
357+
if (_onErrorHandler != null) {
358+
_onErrorHandler(error, [trace.toString()]);
359+
}
360+
if (_onErrorCtrl.hasListener) {
361+
_onErrorCtrl.add(new NgZoneError(error, [trace.toString()]));
362+
}
263363
} else {
264364
throw error;
265365
}

0 commit comments

Comments
 (0)
X Tutup