import React , { useEffect , useCallback , useMemo , useRef , useContext } from 'react' ; import { useThree } from '@react-three/fiber' ; const DEFAULT_WORK_PER_FRAME = 2 ; export type WorkRequester = ( ) => Promise < THREE . WebGLRenderer > ; export const WorkManagerContext = React . createContext < WorkRequester | null > ( null ) ; interface WorkTask { resolve : ( gl : THREE . WebGLRenderer ) => void ; reject : ( error : unknown ) => void ; promise : Promise < unknown > | null ; } export function useWorkRequest ( ) { const requester = useContext ( WorkManagerContext ) ; if ( ! requester ) { throw new Error ( 'must be inside work manager' ) ; } const unmountedCbRef = useRef ( ( ) => { } ) ; useEffect ( ( ) => { return ( ) => { unmountedCbRef . current ( ) ; } ; } , [ ] ) ; const wrappedRequester = useMemo ( ( ) => { const whenUnmounted = new Promise < void > ( ( resolve ) => { unmountedCbRef . current = resolve ; } ) . then ( ( ) => { throw new Error ( 'work requester was unmounted' ) ; } ) ; whenUnmounted . catch ( ( ) => { } ) ; return ( ) => Promise . race < THREE . WebGLRenderer > ( [ whenUnmounted , requester ( ) ] ) ; } , [ requester ] ) ; return wrappedRequester ; } const DUMMY_RESOLVER = ( _gl ) => { } ; const DUMMY_REJECTOR = ( _error ) => { } ; const WorkManager = ( { workPerFrame , children } ) => { const { gl } = useThree ( ) ; const workPerFrameReal = Math . max ( 1 , workPerFrame || DEFAULT_WORK_PER_FRAME ) ; const workPerFrameRef = useRef ( workPerFrameReal ) ; workPerFrameRef . current = workPerFrameReal ; const rafActiveRef = useRef ( false ) ; const pendingTasksRef = useRef < WorkTask [ ] > ( [ ] ) ; const unmountedRef = useRef ( false ) ; useEffect ( ( ) => { return ( ) => { unmountedRef . current = true ; const cleanupList = [ ... pendingTasksRef . current ] ; pendingTasksRef . current . length = 0 ; for ( const task of cleanupList ) { try { task . reject ( new Error ( 'work manager was unmounted while waiting for RAF' ) ) ; } catch ( _error ) { } } } ; } , [ ] ) ; const requestWork = useCallback ( ( ) => { if ( unmountedRef . current ) { throw new Error ( 'work manager is no longer available' ) ; } if ( ! rafActiveRef . current ) { rafActiveRef . current = true ; async function rafRun ( ) { for ( let i = 0 ; i < workPerFrameRef . current ; i += 1 ) { if ( pendingTasksRef . current . length === 0 ) { rafActiveRef . current = false ; return ; } const taskIndex = Math . floor ( Math . random ( ) * pendingTasksRef . current . length ) ; const task = pendingTasksRef . current [ taskIndex ] ; pendingTasksRef . current . splice ( taskIndex , 1 ) ; task . resolve ( gl ) ; await task . promise ; await task . promise ; } requestAnimationFrame ( rafRun ) ; } requestAnimationFrame ( rafRun ) ; } let taskResolve = DUMMY_RESOLVER ; let taskReject = DUMMY_REJECTOR ; const promise = new Promise < THREE . WebGLRenderer > ( ( resolve , reject ) => { taskResolve = resolve ; taskReject = reject ; } ) ; pendingTasksRef . current . push ( { resolve : taskResolve , reject : taskReject , promise : promise } ) ; return promise ; } , [ gl ] ) ; return ( < > < WorkManagerContext . Provider value = { requestWork } > { children } < /WorkManagerContext.Provider> < />); } ; export default WorkManager ;