/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { done } from './util'; function decorate(decorator: (fn: Function, key: string) => Function): Function { return (_target: any, key: string, descriptor: any) => { let fnKey: string | null = null; let fn: Function | null = null; if (typeof descriptor.value === 'function') { fnKey = 'value'; fn = descriptor.value; } else if (typeof descriptor.get === 'function') { fnKey = 'get'; fn = descriptor.get; } if (!fn || !fnKey) { throw new Error('not supported'); } descriptor[fnKey] = decorator(fn, key); }; } function _memoize(fn: Function, key: string): Function { const memoizeKey = `$memoize$${key}`; return function (this: any, ...args: any[]) { if (!this.hasOwnProperty(memoizeKey)) { Object.defineProperty(this, memoizeKey, { configurable: false, enumerable: false, writable: false, value: fn.apply(this, args) }); } return this[memoizeKey]; }; } export const memoize = decorate(_memoize); function _throttle(fn: Function, key: string): Function { const currentKey = `$throttle$current$${key}`; const nextKey = `$throttle$next$${key}`; const trigger = function (this: any, ...args: any[]) { if (this[nextKey]) { return this[nextKey]; } if (this[currentKey]) { this[nextKey] = done(this[currentKey]).then(() => { this[nextKey] = undefined; return trigger.apply(this, args); }); return this[nextKey]; } this[currentKey] = fn.apply(this, args) as Promise; const clear = () => this[currentKey] = undefined; done(this[currentKey]).then(clear, clear); return this[currentKey]; }; return trigger; } export const throttle = decorate(_throttle); function _sequentialize(fn: Function, key: string): Function { const currentKey = `__$sequence$${key}`; return function (this: any, ...args: any[]) { const currentPromise = this[currentKey] as Promise || Promise.resolve(null); const run = async () => await fn.apply(this, args); this[currentKey] = currentPromise.then(run, run); return this[currentKey]; }; } export const sequentialize = decorate(_sequentialize); export function debounce(delay: number): Function { return decorate((fn, key) => { const timerKey = `$debounce$${key}`; return function (this: any, ...args: any[]) { clearTimeout(this[timerKey]); this[timerKey] = setTimeout(() => fn.apply(this, args), delay); }; }); }