Skip to content

Commit 61b423a

Browse files
crisbetojelbourn
authored andcommitted
perf(youtube-player): triggering change detection for unused events (#17665)
Currently the YouTube player component events all trigger change detection and are registered ahead of time, even if they aren't being used. These changes only set up the events if somebody is subscribed to them.
1 parent 7aa522a commit 61b423a

File tree

1 file changed

+30
-25
lines changed

1 file changed

+30
-25
lines changed

src/youtube-player/youtube-player.ts

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ import {
4444
combineLatest as combineLatestOp,
4545
distinctUntilChanged,
4646
filter,
47-
first,
4847
flatMap,
4948
map,
5049
publish,
@@ -236,15 +235,24 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy, OnInit {
236235
}
237236

238237
createEventsBoundInZone(): YT.Events {
239-
return {
240-
onReady: this._runInZone((event) => this.ready.emit(event)),
241-
onStateChange: this._runInZone((event) => this.stateChange.emit(event)),
242-
onPlaybackQualityChange:
243-
this._runInZone((event) => this.playbackQualityChange.emit(event)),
244-
onPlaybackRateChange: this._runInZone((event) => this.playbackRateChange.emit(event)),
245-
onError: this._runInZone((event) => this.error.emit(event)),
246-
onApiChange: this._runInZone((event) => this.apiChange.emit(event)),
247-
};
238+
const output: YT.Events = {};
239+
const events = new Map<keyof YT.Events, EventEmitter<any>>([
240+
['onReady', this.ready],
241+
['onStateChange', this.stateChange],
242+
['onPlaybackQualityChange', this.playbackQualityChange],
243+
['onPlaybackRateChange', this.playbackRateChange],
244+
['onError', this.error],
245+
['onApiChange', this.apiChange]
246+
]);
247+
248+
events.forEach((emitter, name) => {
249+
// Since these events all trigger change detection, only bind them if something is subscribed.
250+
if (emitter.observers.length) {
251+
output[name] = this._runInZone(event => emitter.emit(event));
252+
}
253+
});
254+
255+
return output;
248256
}
249257

250258
ngAfterViewInit() {
@@ -261,9 +269,7 @@ export class YouTubePlayer implements AfterViewInit, OnDestroy, OnInit {
261269

262270
private _runInZone<T extends (...args: any[]) => void>(callback: T):
263271
(...args: Parameters<T>) => void {
264-
return (...args: Parameters<T>) => {
265-
this._ngZone.run(() => callback(...args));
266-
};
272+
return (...args: Parameters<T>) => this._ngZone.run(() => callback(...args));
267273
}
268274

269275
/** Proxied methods. */
@@ -416,19 +422,18 @@ function bindSuggestedQualityToPlayer(
416422
* Returns an observable that emits the loaded player once it's ready. Certain properties/methods
417423
* won't be available until the iframe finishes loading.
418424
*/
419-
function waitUntilReady()
420-
: OperatorFunction<UninitializedPlayer | undefined, Player | undefined> {
425+
function waitUntilReady(): OperatorFunction<UninitializedPlayer | undefined, Player | undefined> {
421426
return flatMap(player => {
422-
if (!player) {
423-
return observableOf<Player|undefined>(undefined);
424-
}
425-
if ('getPlayerStatus' in player) {
426-
return observableOf(player as Player);
427-
}
428-
// The player is not initialized fully until the ready is called.
429-
return fromPlayerOnReady(player)
430-
.pipe(first(), startWith(undefined));
431-
});
427+
if (!player) {
428+
return observableOf<Player|undefined>(undefined);
429+
}
430+
if ('getPlayerStatus' in player) {
431+
return observableOf(player as Player);
432+
}
433+
// The player is not initialized fully until the ready is called.
434+
return fromPlayerOnReady(player)
435+
.pipe(take(1), startWith(undefined));
436+
});
432437
}
433438

434439
/** Since removeEventListener is not on Player when it's initialized, we can't use fromEvent. */

0 commit comments

Comments
 (0)