Skip to content

Instantly share code, notes, and snippets.

@dmail
Last active March 30, 2018 14:58
Show Gist options
  • Select an option

  • Save dmail/e6149a17ffd07016707e7beaed6797a2 to your computer and use it in GitHub Desktop.

Select an option

Save dmail/e6149a17ffd07016707e7beaed6797a2 to your computer and use it in GitHub Desktop.
a more friendly setTimeout
export const delayFunction = ({ fn, ms = 0 } = {}) => {
let delayed = false
let delayedMs
let paused = false
let pausedMs
let id
const callback = () => {
delayed = false
fn()
}
const delay = () => {
if (delayed) {
throw new Error('unexpected call to delay while delayed')
}
delayed = true
delayedMs = Date.now()
id = setTimeout(callback, ms)
}
const isDelayed = () => delayed
const isPaused = () => paused
const cancel = () => {
if (!delayed) {
throw new Error('unexpected call to cancel while not delayed')
}
delayed = false
clearTimeout(id)
}
const pause = () => {
if (!delayed) {
throw new Error('unexpected call to pause while not delayed')
}
if (paused) {
throw new Error('unexpected call to pause while paused')
}
paused = true
pausedMs = Date.now()
clearTimeout(id)
}
const resume = () => {
if (!delayed) {
throw new Error('unexpected call to resume while not delayed')
}
if (!paused) {
throw new Error('unexpected call to resume while not paused')
}
paused = false
const ellapsedMsBetweenDelayedAndPaused = delayedMs - pausedMs
const remainingMs = ms - ellapsedMsBetweenDelayedAndPaused
id = setTimeout(callback, remainingMs)
}
delay()
return Object.freeze({
isDelayed,
isPaused,
cancel,
pause,
resume,
})
}
export const createDelayedFunctionController = () => {
let delayedFunctionHooks
const delay = (...args) => {
if (delayedFunctionHooks) {
delayedFunctionHooks.cancel()
}
delayedFunctionHooks = delayFunction(...args)
}
const isDelayed = () => delayedFunctionHooks ? delayedFunctionHooks.isDelayed() : false
const isPaused = () => delayedFunctionHooks ? delayedFunctionHooks.isPaused() : false
const cancel = () => isDelayed() ? delayedFunctionHooks.cancel() : undefined
const pause = () => isDelayed() && !isPaused() ? delayedFunctionHooks.pause() : undefined
const resume = () => isDelayed() && isPaused() ? delayedFunctionHooks.resume() : undefined
return Object.freeze({
delay,
cancel,
pause,
resume,
isDelayed,
isPaused,
})
}

delayFunction usage

import { delayFunction } from './delayFunction.js'

const delayedFunction = delayFunction({
  fn: () => {},
  ms: 10,
})   

delayedFunction.cancel()      

delayFunction real world usage

Something abstracts delayFunction but exposes cancel method to the outside.

import { delayFunction } from './delayFunction.js'

const MILLISECONDS = 123
let delayedFunction

const doSomething = () => {
  delayedFunction = delayFunction({
    fn: () => {},
    ms: MILLISECONDS,
  })                                   
}

export const cancelSomething = () => {
  if (delayedFunction && delayedFunction.isDelayed()) {
    delayedFunction.cancel()
  }
} 

It works but there is an helper to do this called createDelayedFunctionController.

import { createDelayedFunctionController } from './delayFunction.js'

const MILLISECONDS = 123
const delayedFunctionController = createDelayedFunctionController()

const doSomething = () => {
  delayedFunctionController.delay({
    fn: () => {},
    ms: MILLISECONDS,
  })                                   
}

export const cancelSomething = delayedFunctionController.cancel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment