let global = { stop: false } const timedCaller = (params) => { params.debug && console.log('[DEBUG ' + params.name + '] fn call') // Test condition, if true run the callback if (params.condition.test()) { params.debug && console.log('[DEBUG ' + params.name + '] condition test passed!') if (typeof params.condition.callback === 'function') { params.debug && console.log('[DEBUG ' + params.name + '] callback is fn, should call!') return params.condition.callback() } } // Keep trying as long it does not exceed max timeout params.time.end = new Date().getTime() params.time.totalMs = params.time.end - params.time.start params.debug && console.log('[DEBUG ' + params.name + '] update params.time: ', params.time) if (params.time.totalMs < params.time.maxMs) { params.debug && console.log('[DEBUG ' + params.name + '] did not exceed total timeout') params.timeout = setTimeout(() => { clearTimeout(params.timeout) if (typeof params.stepCallback === 'function') { params.debug && console.log('[DEBUG ' + params.name + '] has a stepCallback and should call') params.setCallback() } params.debug && console.log('[DEBUG ' + params.name + '] should recall / recursive') timedCaller(params) }, params.time.retryAfterMs) } else { params.debug && console.log('[DEBUG ' + params.name + '] timeout exceeded') if (typeof params.time.exceedMaxTimeCallback === 'function') { params.debug && console.log('[DEBUG ' + params.name + '] has exceedMaxTimeCallback and should call') params.time.exceedMaxTimeCallback() } } } timedCaller({ name: 'fnFoobar', time: { start: new Date().getTime(), end: 0, totalMs: 0, maxMs:10000, retryAfterMs: 1000, exceedMaxTimeCallback: () => { console.log('The caller exceedMaxTimeout!') } }, condition: { test: () => (global && global.stop), callback: () => { console.log('Hello world!') } }, debug: true }) console.log('global.stop: ', global.stop) setTimeout(() => { global.stop = true console.log('global.stop: ', global.stop) }, 5000)