UNPKG

angular2

Version:

Angular 2 - a web framework for modern web apps

441 lines 18.1 kB
'use strict';var collection_1 = require('angular2/src/facade/collection'); var lang_1 = require('angular2/src/facade/lang'); var async_1 = require('angular2/src/facade/async'); var profile_1 = require('../profile/profile'); /** * Stores error information; delivered via [NgZone.onError] stream. */ var NgZoneError = (function () { function NgZoneError(error, stackTrace) { this.error = error; this.stackTrace = stackTrace; } return NgZoneError; })(); exports.NgZoneError = NgZoneError; /** * An injectable service for executing work inside or outside of the Angular zone. * * The most common use of this service is to optimize performance when starting a work consisting of * one or more asynchronous tasks that don't require UI updates or error handling to be handled by * Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks * can reenter the Angular zone via {@link #run}. * * <!-- TODO: add/fix links to: * - docs explaining zones and the use of zones in Angular and change-detection * - link to runOutsideAngular/run (throughout this file!) * --> * * ### Example ([live demo](http://plnkr.co/edit/lY9m8HLy7z06vDoUaSN2?p=preview)) * ``` * import {Component, View, NgIf, NgZone} from 'angular2/angular2'; * * @Component({ * selector: 'ng-zone-demo'. * template: ` * <h2>Demo: NgZone</h2> * * <p>Progress: {{progress}}%</p> * <p *ng-if="progress >= 100">Done processing {{label}} of Angular zone!</p> * * <button (click)="processWithinAngularZone()">Process within Angular zone</button> * <button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button> * `, * directives: [NgIf] * }) * export class NgZoneDemo { * progress: number = 0; * label: string; * * constructor(private _ngZone: NgZone) {} * * // Loop inside the Angular zone * // so the UI DOES refresh after each setTimeout cycle * processWithinAngularZone() { * this.label = 'inside'; * this.progress = 0; * this._increaseProgress(() => console.log('Inside Done!')); * } * * // Loop outside of the Angular zone * // so the UI DOES NOT refresh after each setTimeout cycle * processOutsideOfAngularZone() { * this.label = 'outside'; * this.progress = 0; * this._ngZone.runOutsideAngular(() => { * this._increaseProgress(() => { * // reenter the Angular zone and display done * this._ngZone.run(() => {console.log('Outside Done!') }); * }})); * } * * * _increaseProgress(doneCallback: () => void) { * this.progress += 1; * console.log(`Current progress: ${this.progress}%`); * * if (this.progress < 100) { * window.setTimeout(() => this._increaseProgress(doneCallback)), 10) * } else { * doneCallback(); * } * } * } * ``` */ var NgZone = (function () { /** * @param {bool} enableLongStackTrace whether to enable long stack trace. They should only be * enabled in development mode as they significantly impact perf. */ function NgZone(_a) { var enableLongStackTrace = _a.enableLongStackTrace; /** @internal */ this._runScope = profile_1.wtfCreateScope("NgZone#run()"); /** @internal */ this._microtaskScope = profile_1.wtfCreateScope("NgZone#microtask()"); // Number of microtasks pending from _innerZone (& descendants) /** @internal */ this._pendingMicrotasks = 0; // Whether some code has been executed in the _innerZone (& descendants) in the current turn /** @internal */ this._hasExecutedCodeInInnerZone = false; // run() call depth in _mountZone. 0 at the end of a macrotask // zone.run(() => { // top-level call // zone.run(() => {}); // nested call -> in-turn // }); /** @internal */ this._nestedRun = 0; /** @internal */ this._inVmTurnDone = false; /** @internal */ this._pendingTimeouts = []; if (lang_1.global.zone) { this._disabled = false; this._mountZone = lang_1.global.zone; this._innerZone = this._createInnerZone(this._mountZone, enableLongStackTrace); } else { this._disabled = true; this._mountZone = null; } this._onTurnStartEvents = new async_1.EventEmitter(false); this._onTurnDoneEvents = new async_1.EventEmitter(false); this._onEventDoneEvents = new async_1.EventEmitter(false); this._onErrorEvents = new async_1.EventEmitter(false); } /** * Sets the zone hook that is called just before a browser task that is handled by Angular * executes. * * The hook is called once per browser task that is handled by Angular. * * Setting the hook overrides any previously set hook. * * @deprecated this API will be removed in the future. Use `onTurnStart` instead. */ NgZone.prototype.overrideOnTurnStart = function (onTurnStartHook) { this._onTurnStart = lang_1.normalizeBlank(onTurnStartHook); }; Object.defineProperty(NgZone.prototype, "onTurnStart", { /** * Notifies subscribers just before Angular event turn starts. * * Emits an event once per browser task that is handled by Angular. */ get: function () { return this._onTurnStartEvents; }, enumerable: true, configurable: true }); /** @internal */ NgZone.prototype._notifyOnTurnStart = function (parentRun) { var _this = this; parentRun.call(this._innerZone, function () { _this._onTurnStartEvents.emit(null); }); }; /** * Sets the zone hook that is called immediately after Angular zone is done processing the current * task and any microtasks scheduled from that task. * * This is where we typically do change-detection. * * The hook is called once per browser task that is handled by Angular. * * Setting the hook overrides any previously set hook. * * @deprecated this API will be removed in the future. Use `onTurnDone` instead. */ NgZone.prototype.overrideOnTurnDone = function (onTurnDoneHook) { this._onTurnDone = lang_1.normalizeBlank(onTurnDoneHook); }; Object.defineProperty(NgZone.prototype, "onTurnDone", { /** * Notifies subscribers immediately after Angular zone is done processing * the current turn and any microtasks scheduled from that turn. * * Used by Angular as a signal to kick off change-detection. */ get: function () { return this._onTurnDoneEvents; }, enumerable: true, configurable: true }); /** @internal */ NgZone.prototype._notifyOnTurnDone = function (parentRun) { var _this = this; parentRun.call(this._innerZone, function () { _this._onTurnDoneEvents.emit(null); }); }; /** * Sets the zone hook that is called immediately after the `onTurnDone` callback is called and any * microstasks scheduled from within that callback are drained. * * `onEventDoneFn` is executed outside Angular zone, which means that we will no longer attempt to * sync the UI with any model changes that occur within this callback. * * This hook is useful for validating application state (e.g. in a test). * * Setting the hook overrides any previously set hook. * * @deprecated this API will be removed in the future. Use `onEventDone` instead. */ NgZone.prototype.overrideOnEventDone = function (onEventDoneFn, opt_waitForAsync) { var _this = this; if (opt_waitForAsync === void 0) { opt_waitForAsync = false; } var normalizedOnEventDone = lang_1.normalizeBlank(onEventDoneFn); if (opt_waitForAsync) { this._onEventDone = function () { if (!_this._pendingTimeouts.length) { normalizedOnEventDone(); } }; } else { this._onEventDone = normalizedOnEventDone; } }; Object.defineProperty(NgZone.prototype, "onEventDone", { /** * Notifies subscribers immediately after the final `onTurnDone` callback * before ending VM event. * * This event is useful for validating application state (e.g. in a test). */ get: function () { return this._onEventDoneEvents; }, enumerable: true, configurable: true }); /** @internal */ NgZone.prototype._notifyOnEventDone = function () { var _this = this; this.runOutsideAngular(function () { _this._onEventDoneEvents.emit(null); }); }; Object.defineProperty(NgZone.prototype, "hasPendingMicrotasks", { /** * Whether there are any outstanding microtasks. */ get: function () { return this._pendingMicrotasks > 0; }, enumerable: true, configurable: true }); Object.defineProperty(NgZone.prototype, "hasPendingTimers", { /** * Whether there are any outstanding timers. */ get: function () { return this._pendingTimeouts.length > 0; }, enumerable: true, configurable: true }); Object.defineProperty(NgZone.prototype, "hasPendingAsyncTasks", { /** * Whether there are any outstanding asychnronous tasks of any kind that are * scheduled to run within Angular zone. * * Useful as a signal of UI stability. For example, when a test reaches a * point when [hasPendingAsyncTasks] is `false` it might be a good time to run * test expectations. */ get: function () { return this.hasPendingMicrotasks || this.hasPendingTimers; }, enumerable: true, configurable: true }); /** * Sets the zone hook that is called when an error is thrown in the Angular zone. * * Setting the hook overrides any previously set hook. * * @deprecated this API will be removed in the future. Use `onError` instead. */ NgZone.prototype.overrideOnErrorHandler = function (errorHandler) { this._onErrorHandler = lang_1.normalizeBlank(errorHandler); }; Object.defineProperty(NgZone.prototype, "onError", { get: function () { return this._onErrorEvents; }, enumerable: true, configurable: true }); /** * Executes the `fn` function synchronously within the Angular zone and returns value returned by * the function. * * Running functions via `run` allows you to reenter Angular zone from a task that was executed * outside of the Angular zone (typically started via {@link #runOutsideAngular}). * * Any future tasks or microtasks scheduled from within this function will continue executing from * within the Angular zone. */ NgZone.prototype.run = function (fn) { if (this._disabled) { return fn(); } else { var s = this._runScope(); try { return this._innerZone.run(fn); } finally { profile_1.wtfLeave(s); } } }; /** * Executes the `fn` function synchronously in Angular's parent zone and returns value returned by * the function. * * Running functions via `runOutsideAngular` allows you to escape Angular's zone and do work that * doesn't trigger Angular change-detection or is subject to Angular's error handling. * * Any future tasks or microtasks scheduled from within this function will continue executing from * outside of the Angular zone. * * Use {@link #run} to reenter the Angular zone and do work that updates the application model. */ NgZone.prototype.runOutsideAngular = function (fn) { if (this._disabled) { return fn(); } else { return this._mountZone.run(fn); } }; /** @internal */ NgZone.prototype._createInnerZone = function (zone, enableLongStackTrace) { var microtaskScope = this._microtaskScope; var ngZone = this; var errorHandling; if (enableLongStackTrace) { errorHandling = collection_1.StringMapWrapper.merge(Zone.longStackTraceZone, { onError: function (e) { ngZone._notifyOnError(this, e); } }); } else { errorHandling = { onError: function (e) { ngZone._notifyOnError(this, e); } }; } return zone.fork(errorHandling) .fork({ '$run': function (parentRun) { return function () { try { ngZone._nestedRun++; if (!ngZone._hasExecutedCodeInInnerZone) { ngZone._hasExecutedCodeInInnerZone = true; ngZone._notifyOnTurnStart(parentRun); if (ngZone._onTurnStart) { parentRun.call(ngZone._innerZone, ngZone._onTurnStart); } } return parentRun.apply(this, arguments); } finally { ngZone._nestedRun--; // If there are no more pending microtasks, we are at the end of a VM turn (or in // onTurnStart) // _nestedRun will be 0 at the end of a macrotasks (it could be > 0 when there are // nested calls // to run()). if (ngZone._pendingMicrotasks == 0 && ngZone._nestedRun == 0 && !this._inVmTurnDone) { if (ngZone._hasExecutedCodeInInnerZone) { try { this._inVmTurnDone = true; ngZone._notifyOnTurnDone(parentRun); if (ngZone._onTurnDone) { parentRun.call(ngZone._innerZone, ngZone._onTurnDone); } } finally { this._inVmTurnDone = false; ngZone._hasExecutedCodeInInnerZone = false; } } if (ngZone._pendingMicrotasks === 0) { ngZone._notifyOnEventDone(); if (lang_1.isPresent(ngZone._onEventDone)) { ngZone.runOutsideAngular(ngZone._onEventDone); } } } } }; }, '$scheduleMicrotask': function (parentScheduleMicrotask) { return function (fn) { ngZone._pendingMicrotasks++; var microtask = function () { var s = microtaskScope(); try { fn(); } finally { ngZone._pendingMicrotasks--; profile_1.wtfLeave(s); } }; parentScheduleMicrotask.call(this, microtask); }; }, '$setTimeout': function (parentSetTimeout) { return function (fn, delay) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } var id; var cb = function () { fn(); collection_1.ListWrapper.remove(ngZone._pendingTimeouts, id); }; id = parentSetTimeout(cb, delay, args); ngZone._pendingTimeouts.push(id); return id; }; }, '$clearTimeout': function (parentClearTimeout) { return function (id) { parentClearTimeout(id); collection_1.ListWrapper.remove(ngZone._pendingTimeouts, id); }; }, _innerZone: true }); }; /** @internal */ NgZone.prototype._notifyOnError = function (zone, e) { if (lang_1.isPresent(this._onErrorHandler) || async_1.ObservableWrapper.hasSubscribers(this._onErrorEvents)) { var trace = [lang_1.normalizeBlank(e.stack)]; while (zone && zone.constructedAtException) { trace.push(zone.constructedAtException.get()); zone = zone.parent; } if (async_1.ObservableWrapper.hasSubscribers(this._onErrorEvents)) { async_1.ObservableWrapper.callEmit(this._onErrorEvents, new NgZoneError(e, trace)); } if (lang_1.isPresent(this._onErrorHandler)) { this._onErrorHandler(e, trace); } } else { console.log('## _notifyOnError ##'); console.log(e.stack); throw e; } }; return NgZone; })(); exports.NgZone = NgZone; //# sourceMappingURL=ng_zone.js.map