import { AppState } from 'react-native' type Task = () => any | Promise /** * This scheduler runs the supplied task on a scheduled interval. * Unlike `setInterval`, it will ensure that a long running task is done before starting a new one. * @param task the task to run * @param interval the interval * @param foregroundOnly if true, the task will only run when the app is in the foreground * @returns A function to stop the recurring task */ export const scheduleRecurringTask = ( task: Task, interval: number, foregroundOnly = false ) => { let isRunning = true let timeoutId: number | undefined let handleAppStateChangeListener: any = null const taskWrapper = async () => { if (!isRunning) return try { await task() } catch (e) { console.error('An error occured in scheduled task:', e) } finally { if (isRunning) { scheduleNextRun() } } } const handleAppStateChange = async (appState: string) => { if (appState !== 'active') { return } if ( handleAppStateChangeListener && handleAppStateChangeListener.remove && typeof handleAppStateChangeListener.remove === 'function' ) { handleAppStateChangeListener.remove() } else { AppState.removeEventListener('change', handleAppStateChange) } await taskWrapper() } const scheduleNextRun = () => { timeoutId = window.setTimeout(async () => { if (foregroundOnly && AppState.currentState !== 'active') { handleAppStateChangeListener = AppState.addEventListener( 'change', handleAppStateChange ) return } await taskWrapper() }, interval) } scheduleNextRun() const stop = () => { isRunning = false window.clearTimeout(timeoutId) } return stop }