Skip to content

Instantly share code, notes, and snippets.

@joedski
Last active February 9, 2018 01:29
Show Gist options
  • Save joedski/201850f0dff080350778db95fdec7dac to your computer and use it in GitHub Desktop.
Save joedski/201850f0dff080350778db95fdec7dac to your computer and use it in GitHub Desktop.
Simple function to repeat a call until a certain condition is met.
// like repeatUntil but defers actual execution until explicitly called.
// Allows the use of different until conditions, or none at all. (repeat until explicitly canceled.)
function repeater({ interval, repeat }) {
let isCanceled = false;
let until;
function cancel() {
isCanceled = true;
}
function conditionallyCancel() {
if (until()) {
cancel();
}
}
function repeatUntil(nextUntil) {
// If called with a new until condition, use the new one.
until = nextUntil;
// Swallow double calls.
if (!isCanceled) return;
isCanceled = false;
function callRepetition() {
// NOTE: Uncommenting this would result in 0 calls if until() immediately evaluates to true.
// conditionallyCancel();
//
// if (isCanceled) return;
const callTime = Date.now();
return Promise.resolve(repeat())
.then(() => {
conditionallyCancel();
if (isCanceled) return;
const delta = Date.now() - callTime;
// If we took longer, then repeat immediately.
if (delta >= interval) {
callRepetition();
return;
}
// Otherwise, wait the remaining time.
setTimeout(() => {
conditionallyCancel();
if (isCanceled) return;
callRepetition();
}, interval - delta);
})
// Swallowing errors.
.catch((error) => {
cancel();
console.error('Error in repeateUntil:', error);
})
;
}
callRepetition();
}
function repeatUntilCanceled() {
repeatUntil(() => false);
}
return {
cancel,
repeat: repeatUntilCanceled,
repeatUntil,
};
}
function repeatUntil({ interval, repeat, until }) {
// let callTime;
let isCanceled = false;
function cancel() {
isCanceled = true;
}
function conditionallyCancel() {
if (until()) {
cancel();
}
}
function callRepetition() {
// NOTE: Uncommenting this would result in 0 calls if until() immediately evaluates to true.
// conditionallyCancel();
//
// if (isCanceled) return;
const callTime = Date.now();
return Promise.resolve(repeat())
.then(() => {
conditionallyCancel();
if (isCanceled) return;
const delta = Date.now() - callTime;
// If we took longer, then repeat immediately.
if (delta >= interval) {
callRepetition();
return;
}
// Otherwise, wait the remaining time.
setTimeout(() => {
conditionallyCancel();
if (isCanceled) return;
callRepetition();
}, interval - delta);
})
// Swallowing errors.
.catch((error) => {
cancel();
console.error('Error in repeateUntil:', error);
})
;
}
callRepetition();
return {
cancel,
};
}
module.exports = repeatUntil;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment