import type {
Thenable,
FulfilledThenable,
RejectedThenable,
} from 'shared/ReactTypes';
import type {Lane} from './ReactFiberLane';
import type {BatchConfigTransition} from './ReactFiberTracingMarkerComponent';
import {requestTransitionLane} from './ReactFiberRootScheduler';
import {NoLane} from './ReactFiberLane';
import {
hasScheduledTransitionWork,
clearAsyncTransitionTimer,
} from './ReactProfilerTimer';
import {
enableComponentPerformanceTrack,
enableProfilerTimer,
} from 'shared/ReactFeatureFlags';
let currentEntangledListeners: Array<() => mixed> | null = null;
let currentEntangledPendingCount: number = 0;
let currentEntangledLane: Lane = NoLane;
let currentEntangledActionThenable: Thenable<void> | null = null;
export function entangleAsyncAction<S>(
transition: BatchConfigTransition,
thenable: Thenable<S>,
): Thenable<S> {
if (currentEntangledListeners === null) {
const entangledListeners = (currentEntangledListeners = []);
currentEntangledPendingCount = 0;
currentEntangledLane = requestTransitionLane(transition);
const entangledThenable: Thenable<void> = {
status: 'pending',
value: undefined,
then(resolve: void => mixed) {
entangledListeners.push(resolve);
},
};
currentEntangledActionThenable = entangledThenable;
}
currentEntangledPendingCount++;
thenable.then(pingEngtangledActionScope, pingEngtangledActionScope);
return thenable;
}
function pingEngtangledActionScope() {
if (--currentEntangledPendingCount === 0) {
if (enableProfilerTimer && enableComponentPerformanceTrack) {
if (!hasScheduledTransitionWork()) {
clearAsyncTransitionTimer();
}
}
if (currentEntangledListeners !== null) {
if (currentEntangledActionThenable !== null) {
const fulfilledThenable: FulfilledThenable<void> =
(currentEntangledActionThenable: any);
fulfilledThenable.status = 'fulfilled';
}
const listeners = currentEntangledListeners;
currentEntangledListeners = null;
currentEntangledLane = NoLane;
currentEntangledActionThenable = null;
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
}
}
}
export function chainThenableValue<T>(
thenable: Thenable<T>,
result: T,
): Thenable<T> {
const listeners = [];
const thenableWithOverride: Thenable<T> = {
status: 'pending',
value: null,
reason: null,
then(resolve: T => mixed) {
listeners.push(resolve);
},
};
thenable.then(
(value: T) => {
const fulfilledThenable: FulfilledThenable<T> =
(thenableWithOverride: any);
fulfilledThenable.status = 'fulfilled';
fulfilledThenable.value = result;
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener(result);
}
},
error => {
const rejectedThenable: RejectedThenable<T> = (thenableWithOverride: any);
rejectedThenable.status = 'rejected';
rejectedThenable.reason = error;
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener((undefined: any));
}
},
);
return thenableWithOverride;
}
export function peekEntangledActionLane(): Lane {
return currentEntangledLane;
}
export function peekEntangledActionThenable(): Thenable<void> | null {
return currentEntangledActionThenable;
}