fix(segmentation): Allow for debounced pub/sub events to be reliably cancelled when unsubscribed to. (#5092)
This commit is contained in:
parent
a1fb7217bd
commit
38ed7ba744
@ -132,6 +132,10 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
|||||||
},
|
},
|
||||||
getPanelModule,
|
getPanelModule,
|
||||||
onModeExit: ({ servicesManager }: withAppTypes): void => {
|
onModeExit: ({ servicesManager }: withAppTypes): void => {
|
||||||
|
unsubscriptions.forEach(unsubscribe => unsubscribe());
|
||||||
|
// Clear the unsubscriptions
|
||||||
|
unsubscriptions.length = 0;
|
||||||
|
|
||||||
const { cineService, segmentationService } = servicesManager.services;
|
const { cineService, segmentationService } = servicesManager.services;
|
||||||
// Empty out the image load and retrieval pools to prevent memory leaks
|
// Empty out the image load and retrieval pools to prevent memory leaks
|
||||||
// on the mode exits
|
// on the mode exits
|
||||||
@ -150,10 +154,6 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
|||||||
useToggleOneUpViewportGridStore.getState().clearToggleOneUpViewportGridStore();
|
useToggleOneUpViewportGridStore.getState().clearToggleOneUpViewportGridStore();
|
||||||
useSegmentationPresentationStore.getState().clearSegmentationPresentationStore();
|
useSegmentationPresentationStore.getState().clearSegmentationPresentationStore();
|
||||||
segmentationService.removeAllSegmentations();
|
segmentationService.removeAllSegmentations();
|
||||||
|
|
||||||
unsubscriptions.forEach(unsubscribe => unsubscribe());
|
|
||||||
// Clear the unsubscriptions
|
|
||||||
unsubscriptions.length = 0;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -255,7 +255,7 @@ class SegmentationService extends PubSubService {
|
|||||||
this._onSegmentationAddedFromSource
|
this._onSegmentationAddedFromSource
|
||||||
);
|
);
|
||||||
|
|
||||||
this.listeners = {};
|
this.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
public async addSegmentationRepresentation(
|
public async addSegmentationRepresentation(
|
||||||
|
|||||||
@ -9,7 +9,11 @@ export function setupSegmentationDataModifiedHandler({
|
|||||||
customizationService,
|
customizationService,
|
||||||
commandsManager,
|
commandsManager,
|
||||||
}) {
|
}) {
|
||||||
const { unsubscribe } = segmentationService.subscribeDebounced(
|
// A flag to indicate if the event is unsubscribed to. This is important because
|
||||||
|
// the debounced callback does an await and in that period of time the event may have
|
||||||
|
// been unsubscribed.
|
||||||
|
let isUnsubscribed = false;
|
||||||
|
const { unsubscribe: debouncedUnsubscribe } = segmentationService.subscribeDebounced(
|
||||||
segmentationService.EVENTS.SEGMENTATION_DATA_MODIFIED,
|
segmentationService.EVENTS.SEGMENTATION_DATA_MODIFIED,
|
||||||
async ({ segmentationId }) => {
|
async ({ segmentationId }) => {
|
||||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||||
@ -42,7 +46,7 @@ export function setupSegmentationDataModifiedHandler({
|
|||||||
readableText,
|
readableText,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (updatedSegmentation) {
|
if (!isUnsubscribed && updatedSegmentation) {
|
||||||
segmentationService.addOrUpdateSegmentation({
|
segmentationService.addOrUpdateSegmentation({
|
||||||
segmentationId,
|
segmentationId,
|
||||||
segments: updatedSegmentation.segments,
|
segments: updatedSegmentation.segments,
|
||||||
@ -52,6 +56,10 @@ export function setupSegmentationDataModifiedHandler({
|
|||||||
1000
|
1000
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const unsubscribe = () => {
|
||||||
|
isUnsubscribed = true;
|
||||||
|
debouncedUnsubscribe();
|
||||||
|
};
|
||||||
return { unsubscribe };
|
return { unsubscribe };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -54,7 +54,10 @@ function _unsubscribe(eventName, listenerId) {
|
|||||||
|
|
||||||
const listeners = this.listeners[eventName];
|
const listeners = this.listeners[eventName];
|
||||||
if (Array.isArray(listeners)) {
|
if (Array.isArray(listeners)) {
|
||||||
this.listeners[eventName] = listeners.filter(({ id }) => id !== listenerId);
|
this.listeners[eventName] = listeners.filter(({ id, callback }) => {
|
||||||
|
callback?.clearDebounceTimeout?.();
|
||||||
|
return id !== listenerId;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.listeners[eventName] = undefined;
|
this.listeners[eventName] = undefined;
|
||||||
}
|
}
|
||||||
@ -134,6 +137,13 @@ export class PubSubService {
|
|||||||
reset() {
|
reset() {
|
||||||
this.unsubscriptions.forEach(unsub => unsub());
|
this.unsubscriptions.forEach(unsub => unsub());
|
||||||
this.unsubscriptions = [];
|
this.unsubscriptions = [];
|
||||||
|
|
||||||
|
Object.keys(this.listeners).forEach(eventName =>
|
||||||
|
this.listeners[eventName].forEach(({ callback }) => {
|
||||||
|
callback?.clearDebounceTimeout?.();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.listeners = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -2,9 +2,11 @@
|
|||||||
// be triggered. The function will be called after it stops being called for
|
// be triggered. The function will be called after it stops being called for
|
||||||
// N milliseconds. If `immediate` is passed, trigger the function on the
|
// N milliseconds. If `immediate` is passed, trigger the function on the
|
||||||
// leading edge, instead of the trailing.
|
// leading edge, instead of the trailing.
|
||||||
|
// The callback function returned is assigned a clearDebounceTimeout function
|
||||||
|
// that provides for clearing the timeout/debounced function so that it is not called.
|
||||||
function debounce(func, wait, immediate) {
|
function debounce(func, wait, immediate) {
|
||||||
var timeout;
|
var timeout;
|
||||||
return function () {
|
const callback = function () {
|
||||||
var context = this,
|
var context = this,
|
||||||
args = arguments;
|
args = arguments;
|
||||||
var later = function () {
|
var later = function () {
|
||||||
@ -20,6 +22,13 @@ function debounce(func, wait, immediate) {
|
|||||||
func.apply(context, args);
|
func.apply(context, args);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
callback.clearDebounceTimeout = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default debounce;
|
export default debounce;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user