UNPKG

office-ui-fabric-react

Version:

Reusable React components for building experiences for Office 365.

378 lines (376 loc) • 14.6 kB
/** * Bugs often appear in async code when stuff gets disposed, but async operations don't get canceled. * This Async helper class solves these issues by tying async code to the lifetime of a disposable object. * * Usage: Anything class extending from BaseModel can access this helper via this.async. Otherwise create a * new instance of the class and remember to call dispose() during your code's dispose handler. */ "use strict"; var Async = (function () { function Async(parent, onError) { this._timeoutIds = null; this._immediateIds = null; this._intervalIds = null; this._animationFrameIds = null; this._isDisposed = false; this._parent = parent || null; this._onErrorHandler = onError; this._noop = function () { }; } /** * Dispose function, clears all async operations. */ Async.prototype.dispose = function () { var id; this._isDisposed = true; this._parent = null; // Clear timeouts. if (this._timeoutIds) { for (id in this._timeoutIds) { if (this._timeoutIds.hasOwnProperty(id)) { this.clearTimeout(id); } } this._timeoutIds = null; } // Clear immediates. if (this._immediateIds) { for (id in this._immediateIds) { if (this._immediateIds.hasOwnProperty(id)) { this.clearImmediate(id); } } this._immediateIds = null; } // Clear intervals. if (this._intervalIds) { for (id in this._intervalIds) { if (this._intervalIds.hasOwnProperty(id)) { this.clearInterval(id); } } this._intervalIds = null; } // Clear animation frames. if (this._animationFrameIds) { for (id in this._animationFrameIds) { if (this._animationFrameIds.hasOwnProperty(id)) { this.cancelAnimationFrame(id); } } this._animationFrameIds = null; } }; /** * SetTimeout override, which will auto cancel the timeout during dispose. * @param callback Callback to execute. * @param duration Duration in milliseconds. * @return The setTimeout id. */ Async.prototype.setTimeout = function (callback, duration) { var _this = this; var timeoutId = 0; if (!this._isDisposed) { if (!this._timeoutIds) { this._timeoutIds = {}; } /* tslint:disable:ban-native-functions */ timeoutId = setTimeout(function () { // Time to execute the timeout, enqueue it as a foreground task to be executed. try { // Now delete the record and call the callback. delete _this._timeoutIds[timeoutId]; callback.apply(_this._parent); } catch (e) { if (_this._onErrorHandler) { _this._onErrorHandler(e); } } }, duration); /* tslint:enable:ban-native-functions */ this._timeoutIds[timeoutId] = true; } return timeoutId; }; /** * Clears the timeout. * @param id Id to cancel. */ Async.prototype.clearTimeout = function (id) { if (this._timeoutIds && this._timeoutIds[id]) { /* tslint:disable:ban-native-functions */ clearTimeout(id); delete this._timeoutIds[id]; } }; /** * SetImmediate override, which will auto cancel the immediate during dispose. * @param callback Callback to execute. * @return The setTimeout id. */ Async.prototype.setImmediate = function (callback) { var _this = this; var immediateId = 0; if (!this._isDisposed) { if (!this._immediateIds) { this._immediateIds = {}; } /* tslint:disable:ban-native-functions */ var setImmediateCallback = function () { // Time to execute the timeout, enqueue it as a foreground task to be executed. try { // Now delete the record and call the callback. delete _this._immediateIds[immediateId]; callback.apply(_this._parent); } catch (e) { _this._logError(e); } }; immediateId = window.setImmediate ? window.setImmediate(setImmediateCallback) : window.setTimeout(setImmediateCallback, 0); /* tslint:enable:ban-native-functions */ this._immediateIds[immediateId] = true; } return immediateId; }; /** * Clears the immediate. * @param id Id to cancel. */ Async.prototype.clearImmediate = function (id) { if (this._immediateIds && this._immediateIds[id]) { /* tslint:disable:ban-native-functions */ window.clearImmediate ? window.clearImmediate(id) : window.clearTimeout(id); delete this._immediateIds[id]; } }; /** * SetInterval override, which will auto cancel the timeout during dispose. * @param callback Callback to execute. * @param duration Duration in milliseconds. * @return The setTimeout id. */ Async.prototype.setInterval = function (callback, duration) { var _this = this; var intervalId = 0; if (!this._isDisposed) { if (!this._intervalIds) { this._intervalIds = {}; } /* tslint:disable:ban-native-functions */ intervalId = setInterval(function () { // Time to execute the interval callback, enqueue it as a foreground task to be executed. try { callback.apply(_this._parent); } catch (e) { _this._logError(e); } }, duration); /* tslint:enable:ban-native-functions */ this._intervalIds[intervalId] = true; } return intervalId; }; /** * Clears the interval. * @param id Id to cancel. */ Async.prototype.clearInterval = function (id) { if (this._intervalIds && this._intervalIds[id]) { /* tslint:disable:ban-native-functions */ clearInterval(id); delete this._intervalIds[id]; } }; /** * Creates a function that, when executed, will only call the func function at most once per * every wait milliseconds. Provide an options object to indicate that func should be invoked * on the leading and/or trailing edge of the wait timeout. Subsequent calls to the throttled * function will return the result of the last func call. * * Note: If leading and trailing options are true func will be called on the trailing edge of * the timeout only if the the throttled function is invoked more than once during the wait timeout. * * @param func The function to throttle. * @param wait The number of milliseconds to throttle executions to. Defaults to 0. * @param options The options object. * @param options.leading Specify execution on the leading edge of the timeout. * @param options.trailing Specify execution on the trailing edge of the timeout. * @return The new throttled function. */ Async.prototype.throttle = function (func, wait, options) { var _this = this; if (this._isDisposed) { return this._noop; } var waitMS = wait || 0; var leading = true; var trailing = true; var lastExecuteTime = 0; var lastResult; var lastArgs; var timeoutId = null; if (options && typeof (options.leading) === 'boolean') { leading = options.leading; } if (options && typeof (options.trailing) === 'boolean') { trailing = options.trailing; } var callback = function (userCall) { var now = (new Date).getTime(); var delta = now - lastExecuteTime; var waitLength = leading ? waitMS - delta : waitMS; if (delta >= waitMS && (!userCall || leading)) { lastExecuteTime = now; if (timeoutId) { _this.clearTimeout(timeoutId); timeoutId = null; } lastResult = func.apply(_this._parent, lastArgs); } else if (timeoutId === null && trailing) { timeoutId = _this.setTimeout(callback, waitLength); } return lastResult; }; var resultFunction = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i - 0] = arguments[_i]; } lastArgs = args; return callback(true); }; return resultFunction; }; /** * Creates a function that will delay the execution of func until after wait milliseconds have * elapsed since the last time it was invoked. Provide an options object to indicate that func * should be invoked on the leading and/or trailing edge of the wait timeout. Subsequent calls * to the debounced function will return the result of the last func call. * * Note: If leading and trailing options are true func will be called on the trailing edge of * the timeout only if the the debounced function is invoked more than once during the wait * timeout. * * @param func The function to debounce. * @param wait The number of milliseconds to delay. * @param options The options object. * @param options.leading Specify execution on the leading edge of the timeout. * @param options.maxWait The maximum time func is allowed to be delayed before it's called. * @param options.trailing Specify execution on the trailing edge of the timeout. * @return The new debounced function. */ Async.prototype.debounce = function (func, wait, options) { var _this = this; if (this._isDisposed) { return this._noop; } var waitMS = wait || 0; var leading = false; var trailing = true; var maxWait = null; var lastCallTime = 0; var lastExecuteTime = (new Date).getTime(); var lastResult; var lastArgs; var timeoutId = null; if (options && typeof (options.leading) === 'boolean') { leading = options.leading; } if (options && typeof (options.trailing) === 'boolean') { trailing = options.trailing; } if (options && typeof (options.maxWait) === 'number' && !isNaN(options.maxWait)) { maxWait = options.maxWait; } var callback = function (userCall) { var now = (new Date).getTime(); var executeImmediately = false; if (userCall) { if (leading && now - lastCallTime >= waitMS) { executeImmediately = true; } lastCallTime = now; } var delta = now - lastCallTime; var waitLength = waitMS - delta; var maxWaitDelta = now - lastExecuteTime; var maxWaitExpired = false; if (maxWait !== null) { // maxWait only matters when there is a pending callback if (maxWaitDelta >= maxWait && timeoutId) { maxWaitExpired = true; } else { waitLength = Math.min(waitLength, maxWait - maxWaitDelta); } } if (delta >= waitMS || maxWaitExpired || executeImmediately) { if (timeoutId) { _this.clearTimeout(timeoutId); timeoutId = null; } lastExecuteTime = now; lastResult = func.apply(_this._parent, lastArgs); } else if ((timeoutId === null || !userCall) && trailing) { timeoutId = _this.setTimeout(callback, waitLength); } return lastResult; }; var resultFunction = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i - 0] = arguments[_i]; } lastArgs = args; return callback(true); }; return resultFunction; }; Async.prototype.requestAnimationFrame = function (callback) { var _this = this; var animationFrameId = 0; if (!this._isDisposed) { if (!this._animationFrameIds) { this._animationFrameIds = {}; } /* tslint:disable:ban-native-functions */ var animationFrameCallback = function () { try { // Now delete the record and call the callback. delete _this._animationFrameIds[animationFrameId]; callback.apply(_this._parent); } catch (e) { _this._logError(e); } }; animationFrameId = window.requestAnimationFrame ? window.requestAnimationFrame(animationFrameCallback) : window.setTimeout(animationFrameCallback, 0); /* tslint:enable:ban-native-functions */ this._animationFrameIds[animationFrameId] = true; } return animationFrameId; }; Async.prototype.cancelAnimationFrame = function (id) { if (this._animationFrameIds && this._animationFrameIds[id]) { /* tslint:disable:ban-native-functions */ window.cancelAnimationFrame ? window.cancelAnimationFrame(id) : window.clearTimeout(id); /* tslint:enable:ban-native-functions */ delete this._animationFrameIds[id]; } }; Async.prototype._logError = function (e) { if (this._onErrorHandler) { this._onErrorHandler(e); } }; return Async; }()); exports.Async = Async; //# sourceMappingURL=Async.js.map