diff --git a/packages/core/data/observable/index.ts b/packages/core/data/observable/index.ts index 8f75ff67d0..879a13f551 100644 --- a/packages/core/data/observable/index.ts +++ b/packages/core/data/observable/index.ts @@ -42,6 +42,10 @@ interface ListenerEntry { once?: true; } +interface ListEntryMap { + [eventName: string]: Array; +} + let _wrappedIndex = 0; /** @@ -107,7 +111,7 @@ export class Observable { */ public _isViewBase: boolean; - private readonly _observers: { [eventName: string]: Array } = {}; + private readonly _observers: ListEntryMap = {}; /** * Gets the value of the specified property. @@ -379,7 +383,7 @@ export class Observable { const eventName = data.eventName + eventType; const entries = _globalEventHandlers[eventClass][eventName]; if (entries) { - Observable._handleEvent(entries, data); + Observable._fireEvent(entries, data); } } @@ -388,7 +392,7 @@ export class Observable { const eventName = data.eventName + eventType; const entries = _globalEventHandlers['*'][eventName]; if (entries) { - Observable._handleEvent(entries, data); + Observable._fireEvent(entries, data); } } } @@ -411,35 +415,49 @@ export class Observable { const observers = this._observers[data.eventName]; if (observers) { - Observable._handleEvent(observers, dataWithObject); + Observable._fireEvent(observers, dataWithObject); } this._globalNotify(eventClass, '', dataWithObject); } - private static _handleEvent(observers: Array, data: T): void { - if (!observers.length) { + private static _fireEvent(observers: Array, data: T): void { + const length = observers.length; + + if (!length) { return; } - for (let i = observers.length - 1; i >= 0; i--) { - const entry = observers[i]; - if (!entry) { - continue; - } + if (length === 1) { + const index = 0; + this._handleListenerEntry(observers[index], index, observers, data); + } else { + // This keeps a copy of observers list to ensure concurrency + const observersCp = observers.slice(); - if (entry.once) { - observers.splice(i, 1); + for (let i = 0; i < length; i++) { + const entry = observersCp[i]; + this._handleListenerEntry(entry, i, observers, data); } + } + } - const returnValue = entry.thisArg ? entry.callback.apply(entry.thisArg, [data]) : entry.callback(data); + private static _handleListenerEntry(entry: ListenerEntry, index: number, observers: Array, data: T): void { + if (!entry) { + return; + } - // This ensures errors thrown inside asynchronous functions do not get swallowed - if (returnValue instanceof Promise) { - returnValue.catch((err) => { - console.error(err); - }); - } + if (entry.once) { + observers.splice(index, 1); + } + + const returnValue = entry.thisArg ? entry.callback.apply(entry.thisArg, [data]) : entry.callback(data); + + // This ensures errors thrown inside asynchronous functions do not get swallowed + if (returnValue instanceof Promise) { + returnValue.catch((err) => { + console.error(err); + }); } } diff --git a/packages/core/ui/core/view/index.android.ts b/packages/core/ui/core/view/index.android.ts index 5fe18d0bac..68e835956c 100644 --- a/packages/core/ui/core/view/index.android.ts +++ b/packages/core/ui/core/view/index.android.ts @@ -694,12 +694,34 @@ export class View extends ViewCommon { } public handleGestureTouch(event: android.view.MotionEvent): any { - for (const type in this._gestureObservers) { - const list = this._gestureObservers[type]; - list.forEach((element) => { - element.androidOnTouchEvent(event); - }); + // This keeps a copy of gesture observers from the map to ensure concurrency + const allObservers = Object.values(this._gestureObservers); + + for (const observers of allObservers) { + const length = observers.length; + + if (!length) { + continue; + } + + if (length === 1) { + const entry = observers[0]; + if (entry) { + entry.androidOnTouchEvent(event); + } + } else { + // This keeps a copy of gesture observers list to ensure concurrency + const observersCp = observers.slice(); + + for (let i = 0; i < length; i++) { + const entry = observersCp[i]; + if (entry) { + entry.androidOnTouchEvent(event); + } + } + } } + if (this.parent instanceof View) { this.parent.handleGestureTouch(event); } diff --git a/packages/core/ui/gestures/index.android.ts b/packages/core/ui/gestures/index.android.ts index 6f547b3cdb..4c7c8f0ff9 100644 --- a/packages/core/ui/gestures/index.android.ts +++ b/packages/core/ui/gestures/index.android.ts @@ -441,7 +441,7 @@ function _getPanArgs(deltaX: number, deltaY: number, view: View, state: GestureS function _executeCallback(observer: GesturesObserver, args: GestureEventData) { if (observer && observer.callback) { - observer.callback.call((observer)._context, args); + observer.callback.call(observer.context, args); } }