mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 02:29:24 +00:00
Compare commits
4 Commits
master
...
performanc
Author | SHA1 | Date | |
---|---|---|---|
21ce86b49e | |||
df83a4fd1c | |||
bb7fecdcee | |||
9385b8ce99 |
@ -48,6 +48,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import throttle from '../../../utils/throttle';
|
||||
const waitForMs = 1000;
|
||||
const CONTEXT_MENU_ACTIONS = ['viewDatumAction', 'viewHistoricalData', 'remove'];
|
||||
const BLANK_VALUE = '---';
|
||||
|
||||
@ -189,6 +191,7 @@ export default {
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.updateView = throttle(this.updateView.bind(this), waitForMs);
|
||||
this.metadata = this.openmct.telemetry.getMetadata(this.domainObject);
|
||||
this.formats = this.openmct.telemetry.getFormatMap(this.metadata);
|
||||
this.keyString = this.openmct.objects.makeKeyString(this.domainObject.identifier);
|
||||
@ -241,13 +244,14 @@ export default {
|
||||
methods: {
|
||||
updateView() {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = this.renderWhenVisible(() => {
|
||||
this.timestamp = this.getParsedTimestamp(this.latestDatum);
|
||||
this.datum = this.latestDatum;
|
||||
this.updatingView = false;
|
||||
});
|
||||
this.updatingView = this.renderWhenVisible(this.updateDatum);
|
||||
}
|
||||
},
|
||||
updateDatum() {
|
||||
this.timestamp = this.getParsedTimestamp(this.latestDatum);
|
||||
this.datum = this.latestDatum;
|
||||
this.updatingView = false;
|
||||
},
|
||||
clickedRow(event) {
|
||||
if (this.openmct.editor.isEditing()) {
|
||||
event.preventDefault();
|
||||
|
@ -39,7 +39,9 @@
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import momentTimezone from 'moment-timezone';
|
||||
import raf from 'utils/raf';
|
||||
|
||||
import throttle from '../../../utils/throttle';
|
||||
const waitForMs = 1000;
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'domainObject'],
|
||||
@ -88,7 +90,7 @@ export default {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
);
|
||||
this.tick = raf(this.tick);
|
||||
this.tick = throttle(this.tick.bind(this), waitForMs);
|
||||
this.openmct.time.on('tick', this.tick);
|
||||
},
|
||||
beforeUnmount() {
|
||||
|
@ -30,7 +30,9 @@
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import raf from 'utils/raf';
|
||||
|
||||
import throttle from '../../../utils/throttle';
|
||||
const waitForMs = 1000;
|
||||
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
@ -46,7 +48,7 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.tick = raf(this.tick);
|
||||
this.tick = throttle(this.tick.bind(this), waitForMs);
|
||||
this.openmct.time.on('tick', this.tick);
|
||||
this.tick(this.timeTextValue);
|
||||
},
|
||||
|
@ -74,6 +74,8 @@ import {
|
||||
import stalenessMixin from '@/ui/mixins/staleness-mixin';
|
||||
|
||||
import tooltipHelpers from '../../../api/tooltips/tooltipMixins';
|
||||
import throttle from '../../../utils/throttle';
|
||||
const waitForMs = 1000;
|
||||
import conditionalStylesMixin from '../mixins/objectStyles-mixin';
|
||||
import LayoutFrame from './LayoutFrame.vue';
|
||||
|
||||
@ -236,6 +238,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.updateView = throttle(this.updateView.bind(this), waitForMs);
|
||||
this.getAndSetObject();
|
||||
|
||||
this.status = this.openmct.status.get(this.item.identifier);
|
||||
@ -294,12 +297,13 @@ export default {
|
||||
},
|
||||
updateView() {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = this.renderWhenVisible(() => {
|
||||
this.datum = this.latestDatum;
|
||||
this.updatingView = false;
|
||||
});
|
||||
this.updatingView = this.renderWhenVisible(this.updateDatum);
|
||||
}
|
||||
},
|
||||
updateDatum() {
|
||||
this.datum = this.latestDatum;
|
||||
this.updatingView = false;
|
||||
},
|
||||
refreshData(bounds, isTick) {
|
||||
if (!isTick) {
|
||||
this.latestDatum = undefined;
|
||||
|
@ -733,11 +733,12 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isRendering = this.renderWhenVisible(() => {
|
||||
this.isRendering = false;
|
||||
this.isRendering = this.renderWhenVisible(this.updateCurrentValue);
|
||||
},
|
||||
updateCurrentValue() {
|
||||
this.isRendering = false;
|
||||
|
||||
this.curVal = this.round(this.formats[this.valueKey].format(this.datum), this.precision);
|
||||
});
|
||||
this.curVal = this.round(this.formats[this.valueKey].format(this.datum), this.precision);
|
||||
},
|
||||
valToPercent(vValue) {
|
||||
// Used by dial
|
||||
|
@ -322,14 +322,14 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
sizedImageDimensions() {
|
||||
this.debounceResizeSvg();
|
||||
this.throttleResizeSvg();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.debounceResizeSvg = throttle(this.resizeSvg, 100);
|
||||
this.throttleResizeSvg = throttle(this.resizeSvg.bind(this), 100);
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.debounceResizeSvg();
|
||||
this.throttleResizeSvg();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
|
@ -19,7 +19,8 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import raf from '@/utils/raf';
|
||||
import throttle from '../../utils/throttle';
|
||||
const waitForMs = 1000;
|
||||
|
||||
export default class AbstractStatusIndicator {
|
||||
#popupComponent;
|
||||
@ -36,8 +37,7 @@ export default class AbstractStatusIndicator {
|
||||
|
||||
this.showPopup = this.showPopup.bind(this);
|
||||
this.clearPopup = this.clearPopup.bind(this);
|
||||
this.positionBox = this.positionBox.bind(this);
|
||||
this.positionBox = raf(this.positionBox);
|
||||
this.positionBox = throttle(this.positionBox.bind(this), waitForMs);
|
||||
|
||||
this.#indicator = this.createIndicator();
|
||||
this.#popupComponent = this.createPopupComponent();
|
||||
|
@ -284,6 +284,7 @@ import CSVExporter from '../../../exporters/CSVExporter.js';
|
||||
import ProgressBar from '../../../ui/components/ProgressBar.vue';
|
||||
import Search from '../../../ui/components/SearchComponent.vue';
|
||||
import ToggleSwitch from '../../../ui/components/ToggleSwitch.vue';
|
||||
import throttle from '../../../utils/throttle';
|
||||
import SizingRow from './SizingRow.vue';
|
||||
import TableColumnHeader from './TableColumnHeader.vue';
|
||||
import TableFooterIndicator from './TableFooterIndicator.vue';
|
||||
@ -293,6 +294,7 @@ const VISIBLE_ROW_COUNT = 100;
|
||||
const ROW_HEIGHT = 17;
|
||||
const RESIZE_POLL_INTERVAL = 200;
|
||||
const AUTO_SCROLL_TRIGGER_HEIGHT = 100;
|
||||
const waitForMs = 1000;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -481,9 +483,9 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.csvExporter = new CSVExporter();
|
||||
this.rowsAdded = _.throttle(this.rowsAdded, 200);
|
||||
this.rowsRemoved = _.throttle(this.rowsRemoved, 200);
|
||||
this.scroll = _.throttle(this.scroll, 100);
|
||||
this.rowsAdded = throttle(this.rowsAdded.bind(this), waitForMs);
|
||||
this.rowsRemoved = throttle(this.rowsRemoved.bind(this), waitForMs);
|
||||
this.scroll = throttle(this.scroll.bind(this), waitForMs);
|
||||
|
||||
if (!this.marking.useAlternateControlBar && !this.enableLegacyToolbar) {
|
||||
this.$nextTick(() => {
|
||||
@ -549,41 +551,42 @@ export default {
|
||||
methods: {
|
||||
updateVisibleRows() {
|
||||
if (!this.updatingView) {
|
||||
this.updatingView = this.renderWhenVisible(() => {
|
||||
let start = 0;
|
||||
let end = VISIBLE_ROW_COUNT;
|
||||
let tableRows = this.table.tableRows.getRows();
|
||||
let tableRowsLength = tableRows.length;
|
||||
|
||||
this.totalNumberOfRows = tableRowsLength;
|
||||
|
||||
if (tableRowsLength < VISIBLE_ROW_COUNT) {
|
||||
end = tableRowsLength;
|
||||
} else {
|
||||
let firstVisible = this.calculateFirstVisibleRow();
|
||||
let lastVisible = this.calculateLastVisibleRow();
|
||||
let totalVisible = lastVisible - firstVisible;
|
||||
|
||||
let numberOffscreen = VISIBLE_ROW_COUNT - totalVisible;
|
||||
start = firstVisible - Math.floor(numberOffscreen / 2);
|
||||
end = lastVisible + Math.ceil(numberOffscreen / 2);
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
end = Math.min(VISIBLE_ROW_COUNT, tableRowsLength);
|
||||
} else if (end >= tableRowsLength) {
|
||||
end = tableRowsLength;
|
||||
start = end - VISIBLE_ROW_COUNT + 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.rowOffset = start;
|
||||
this.visibleRows = tableRows.slice(start, end);
|
||||
|
||||
this.updatingView = false;
|
||||
});
|
||||
this.updatingView = this.renderWhenVisible(this.setVisibleRows);
|
||||
}
|
||||
},
|
||||
setVisibleRows() {
|
||||
let start = 0;
|
||||
let end = VISIBLE_ROW_COUNT;
|
||||
let tableRows = this.table.tableRows.getRows();
|
||||
let tableRowsLength = tableRows.length;
|
||||
|
||||
this.totalNumberOfRows = tableRowsLength;
|
||||
|
||||
if (tableRowsLength < VISIBLE_ROW_COUNT) {
|
||||
end = tableRowsLength;
|
||||
} else {
|
||||
let firstVisible = this.calculateFirstVisibleRow();
|
||||
let lastVisible = this.calculateLastVisibleRow();
|
||||
let totalVisible = lastVisible - firstVisible;
|
||||
|
||||
let numberOffscreen = VISIBLE_ROW_COUNT - totalVisible;
|
||||
start = firstVisible - Math.floor(numberOffscreen / 2);
|
||||
end = lastVisible + Math.ceil(numberOffscreen / 2);
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
end = Math.min(VISIBLE_ROW_COUNT, tableRowsLength);
|
||||
} else if (end >= tableRowsLength) {
|
||||
end = tableRowsLength;
|
||||
start = end - VISIBLE_ROW_COUNT + 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.rowOffset = start;
|
||||
this.visibleRows = tableRows.slice(start, end);
|
||||
|
||||
this.updatingView = false;
|
||||
},
|
||||
calculateFirstVisibleRow() {
|
||||
let scrollTop = this.scrollable.scrollTop;
|
||||
|
||||
@ -822,30 +825,31 @@ export default {
|
||||
this.isDropTargetActive = isActive;
|
||||
},
|
||||
pollForResize() {
|
||||
this.resizePollHandle = setInterval(() => {
|
||||
this.renderWhenVisible(this.calculateTableSizeAndSetScrollTop.bind(this));
|
||||
}, RESIZE_POLL_INTERVAL);
|
||||
},
|
||||
calculateTableSizeAndSetScrollTop() {
|
||||
let el = this.$refs.root;
|
||||
let width = el.clientWidth;
|
||||
let height = el.clientHeight;
|
||||
let scrollTop = this.scrollable.scrollTop;
|
||||
|
||||
this.resizePollHandle = setInterval(() => {
|
||||
this.renderWhenVisible(() => {
|
||||
if ((el.clientWidth !== width || el.clientHeight !== height) && this.isAutosizeEnabled) {
|
||||
this.calculateTableSize();
|
||||
// On some resize events scrollTop is reset to 0. Possibly due to a transition we're using?
|
||||
// Need to preserve scroll position in this case.
|
||||
if (this.autoScroll) {
|
||||
this.scrollToBottom();
|
||||
} else {
|
||||
this.scrollable.scrollTop = scrollTop;
|
||||
}
|
||||
if ((el.clientWidth !== width || el.clientHeight !== height) && this.isAutosizeEnabled) {
|
||||
this.calculateTableSize();
|
||||
// On some resize events scrollTop is reset to 0. Possibly due to a transition we're using?
|
||||
// Need to preserve scroll position in this case.
|
||||
if (this.autoScroll) {
|
||||
this.scrollToBottom();
|
||||
} else {
|
||||
this.scrollable.scrollTop = scrollTop;
|
||||
}
|
||||
|
||||
width = el.clientWidth;
|
||||
height = el.clientHeight;
|
||||
}
|
||||
width = el.clientWidth;
|
||||
height = el.clientHeight;
|
||||
}
|
||||
|
||||
scrollTop = this.scrollable.scrollTop;
|
||||
});
|
||||
}, RESIZE_POLL_INTERVAL);
|
||||
scrollTop = this.scrollable.scrollTop;
|
||||
},
|
||||
clearRowsAndRerender() {
|
||||
this.visibleRows = [];
|
||||
|
@ -81,6 +81,7 @@ import {
|
||||
REALTIME_MODE_KEY,
|
||||
TIME_CONTEXT_EVENTS
|
||||
} from '../../api/time/constants';
|
||||
import throttle from '../../utils/throttle';
|
||||
import ConductorAxis from './ConductorAxis.vue';
|
||||
import ConductorClock from './ConductorClock.vue';
|
||||
import ConductorInputsFixed from './ConductorInputsFixed.vue';
|
||||
@ -155,8 +156,9 @@ export default {
|
||||
document.addEventListener('keyup', this.handleKeyUp);
|
||||
|
||||
this.setTimeSystem(this.copy(this.openmct.time.getTimeSystem()));
|
||||
this.handleNewBounds = throttle(this.handleNewBounds.bind(this), 1000);
|
||||
|
||||
this.openmct.time.on(TIME_CONTEXT_EVENTS.boundsChanged, _.throttle(this.handleNewBounds, 300));
|
||||
this.openmct.time.on(TIME_CONTEXT_EVENTS.boundsChanged, this.handleNewBounds);
|
||||
this.openmct.time.on(TIME_CONTEXT_EVENTS.timeSystemChanged, this.setTimeSystem);
|
||||
this.openmct.time.on(TIME_CONTEXT_EVENTS.modeChanged, this.setMode);
|
||||
},
|
||||
@ -164,7 +166,7 @@ export default {
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
document.removeEventListener('keyup', this.handleKeyUp);
|
||||
|
||||
this.openmct.time.off(TIME_CONTEXT_EVENTS.boundsChanged, _.throttle(this.handleNewBounds, 300));
|
||||
this.openmct.time.off(TIME_CONTEXT_EVENTS.boundsChanged, this.handleNewBounds);
|
||||
this.openmct.time.off(TIME_CONTEXT_EVENTS.timeSystemChanged, this.setTimeSystem);
|
||||
this.openmct.time.off(TIME_CONTEXT_EVENTS.modeChanged, this.setMode);
|
||||
},
|
||||
|
@ -51,6 +51,7 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
import { TIME_CONTEXT_EVENTS } from '../../api/time/constants';
|
||||
import throttle from '../../utils/throttle';
|
||||
import TimePopupFixed from './TimePopupFixed.vue';
|
||||
|
||||
export default {
|
||||
@ -123,7 +124,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.handleNewBounds = _.throttle(this.handleNewBounds, 300);
|
||||
this.handleNewBounds = throttle(this.handleNewBounds.bind(this), 1000);
|
||||
this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.getTimeSystem())));
|
||||
this.openmct.time.on(TIME_CONTEXT_EVENTS.timeSystemChanged, this.setTimeSystem);
|
||||
this.setTimeContext();
|
||||
|
@ -58,6 +58,7 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
import { TIME_CONTEXT_EVENTS } from '../../api/time/constants';
|
||||
import throttle from '../../utils/throttle';
|
||||
import TimePopupRealtime from './TimePopupRealtime.vue';
|
||||
|
||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||
@ -145,7 +146,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.handleNewBounds = _.throttle(this.handleNewBounds, 300);
|
||||
this.handleNewBounds = throttle(this.handleNewBounds.bind(this), 1000);
|
||||
this.setTimeSystem(this.copy(this.openmct.time.getTimeSystem()));
|
||||
this.openmct.time.on(TIME_CONTEXT_EVENTS.timeSystemChanged, this.setTimeSystem);
|
||||
this.setTimeContext();
|
||||
|
@ -96,6 +96,7 @@
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
|
||||
import throttle from '../../utils/throttle';
|
||||
import DatePicker from './DatePicker.vue';
|
||||
|
||||
const DEFAULT_DURATION_FORMATTER = 'duration';
|
||||
@ -156,7 +157,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.handleNewBounds = _.throttle(this.handleNewBounds, 300);
|
||||
this.handleNewBounds = throttle(this.handleNewBounds.bind(this), 1000);
|
||||
this.setTimeSystem(JSON.parse(JSON.stringify(this.openmct.time.getTimeSystem())));
|
||||
},
|
||||
beforeUnmount() {
|
||||
|
@ -19,7 +19,8 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import raf from '@/utils/raf';
|
||||
import throttle from '../../utils/throttle';
|
||||
const waitForMs = 1000;
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'configuration'],
|
||||
@ -32,7 +33,7 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.positionBox = raf(this.positionBox);
|
||||
this.positionBox = throttle(this.positionBox.bind(this), waitForMs);
|
||||
this.timeConductorOptionsHolder = this.$el;
|
||||
this.timeConductorOptionsHolder.addEventListener('click', this.showPopup);
|
||||
},
|
||||
|
@ -20,7 +20,9 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
import debounce from '@/utils/debounce';
|
||||
import raf from '@/utils/raf';
|
||||
|
||||
import throttle from '../../../utils/throttle';
|
||||
const waitForMs = 1000;
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@ -38,7 +40,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.positionBox = debounce(raf(this.positionBox), 250);
|
||||
this.positionBox = debounce(throttle(this.positionBox.bind(this), waitForMs), 250);
|
||||
this.timeConductorOptionsHolder = this.$el;
|
||||
this.timeConductorOptionsHolder.addEventListener('click', this.showPopup);
|
||||
},
|
||||
|
@ -38,6 +38,7 @@ import { v4 as uuid } from 'uuid';
|
||||
import { TIME_CONTEXT_EVENTS } from '../../api/time/constants';
|
||||
import ListView from '../../ui/components/List/ListView.vue';
|
||||
import { getPreciseDuration } from '../../utils/duration';
|
||||
import throttle from '../../utils/throttle';
|
||||
import { getValidatedData } from '../plan/util';
|
||||
import { SORT_ORDER_OPTIONS } from './constants';
|
||||
|
||||
@ -116,7 +117,7 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.isEditing = this.openmct.editor.isEditing();
|
||||
this.updateTimestamp = _.throttle(this.updateTimestamp, 1000);
|
||||
this.updateTimestamp = throttle(this.updateTimestamp.bind(this), 1000);
|
||||
|
||||
this.setTimeContext();
|
||||
this.timestamp = this.timeContext.now();
|
||||
|
@ -44,13 +44,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import raf from 'utils/raf';
|
||||
|
||||
import throttle from '../../../utils/throttle';
|
||||
|
||||
const moment = require('moment-timezone');
|
||||
const momentDurationFormatSetup = require('moment-duration-format');
|
||||
const refreshRateSeconds = 2;
|
||||
const waitForMs = 1000;
|
||||
|
||||
momentDurationFormatSetup(moment);
|
||||
|
||||
@ -176,8 +175,11 @@ export default {
|
||||
this.triggerAction(`timer.${timerAction}`);
|
||||
}
|
||||
|
||||
this.handleTick = raf(this.handleTick);
|
||||
this.refreshTimerObject = throttle(this.refreshTimerObject, refreshRateSeconds * 1000);
|
||||
this.handleTick = throttle(this.handleTick.bind(this), waitForMs);
|
||||
this.refreshTimerObject = throttle(
|
||||
this.refreshTimerObject.bind(this),
|
||||
refreshRateSeconds * 1000
|
||||
);
|
||||
this.openmct.time.on('tick', this.handleTick);
|
||||
|
||||
this.viewActionsCollection = this.openmct.actions.getActionsCollection(
|
||||
|
@ -1,10 +1,11 @@
|
||||
import raf from './raf';
|
||||
import throttle from './throttle';
|
||||
const waitForMs = 1000;
|
||||
|
||||
describe('The raf utility function', () => {
|
||||
it('Throttles function calls that arrive in quick succession using Request Animation Frame', () => {
|
||||
const unthrottledFunction = jasmine.createSpy('unthrottledFunction');
|
||||
const throttledCallback = jasmine.createSpy('throttledCallback');
|
||||
const throttledFunction = raf(throttledCallback);
|
||||
const throttledFunction = throttle(throttledCallback, waitForMs);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
unthrottledFunction();
|
||||
@ -20,7 +21,7 @@ describe('The raf utility function', () => {
|
||||
});
|
||||
it('Only invokes callback once per animation frame', () => {
|
||||
const throttledCallback = jasmine.createSpy('throttledCallback');
|
||||
const throttledFunction = raf(throttledCallback);
|
||||
const throttledFunction = throttle(throttledCallback, waitForMs);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
throttledFunction();
|
||||
@ -40,7 +41,7 @@ describe('The raf utility function', () => {
|
||||
});
|
||||
it('Invokes callback again if called in subsequent animation frame', () => {
|
||||
const throttledCallback = jasmine.createSpy('throttledCallback');
|
||||
const throttledFunction = raf(throttledCallback);
|
||||
const throttledFunction = throttle(throttledCallback, waitForMs);
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
throttledFunction();
|
||||
|
@ -6,29 +6,32 @@
|
||||
* @return {Function} Returns the new throttled function.
|
||||
*/
|
||||
export default function throttle(func, wait) {
|
||||
let timeout;
|
||||
let result;
|
||||
let previous = 0;
|
||||
let waiting = false;
|
||||
let lastArgs = null;
|
||||
|
||||
// If the function was invoked while we were waiting, call it with those arguments
|
||||
// If it wasn't called when we were waiting, we're done
|
||||
function checkFunctionInvokedOrStopWaiting() {
|
||||
if (lastArgs !== null) {
|
||||
func(...lastArgs);
|
||||
lastArgs = null;
|
||||
setTimeout(checkFunctionInvokedOrStopWaiting, wait);
|
||||
} else {
|
||||
waiting = false;
|
||||
}
|
||||
}
|
||||
|
||||
return function (...args) {
|
||||
const now = new Date().getTime();
|
||||
const remaining = wait - (now - previous);
|
||||
|
||||
if (remaining <= 0 || remaining > wait) {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
|
||||
previous = now;
|
||||
result = func(...args);
|
||||
} else if (!timeout) {
|
||||
timeout = setTimeout(() => {
|
||||
previous = new Date().getTime();
|
||||
timeout = null;
|
||||
result = func(...args);
|
||||
}, remaining);
|
||||
// if we're waiting (in between calls), store the arguments to use for when we call the function after we're done waiting
|
||||
if (waiting) {
|
||||
lastArgs = args;
|
||||
return;
|
||||
}
|
||||
return result;
|
||||
|
||||
// if we're not waiting (between calls), call the function and wait
|
||||
func(...args);
|
||||
waiting = true;
|
||||
// when we're done waiting, now check if the function was invoked while we were waiting
|
||||
setTimeout(checkFunctionInvokedOrStopWaiting, wait);
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user