Skip to content

Instantly share code, notes, and snippets.

@roychri
Created February 13, 2018 03:34
Show Gist options
  • Save roychri/1b7c7cc347b80d01f7e257bb0d6e6b8f to your computer and use it in GitHub Desktop.
Save roychri/1b7c7cc347b80d01f7e257bb0d6e6b8f to your computer and use it in GitHub Desktop.
Promise based repeater that wont start the loop unless the callback completed

This is an implementation similar to setInternal() but will not start the delay/loop again until the callback has finished (presuming it returned a promise). Im interested to know if there's a better way to do this. I choose this rather than using setInterval() because I wanted to make sure it would not start over if the callback took longer than the interval time.

The callback function will only be called once an initial delay has passed. It would be simple to add a call to cb() before calling the loop.

Let me know what you think!

const Timers = require( './timers' );
let repeatId = Timers.repeat( 250, () => {
return Timers.wait( 2 ); // Simulate a long IO operation
});
Timers.wait( 1 ).then( () => {
Timers.clearRepeat( repeatId ); // Clearning it before the long operation finishes will not prevent it to finish
});
const uuid = require( 'uuid' );
var repeats = {};
exports.repeat = function( delayMs, cb ) {
let id = uuid.v4();
var cancelMe;
var cancellable = new Promise( ( resolve, reject ) => {
cancelMe = function() {
repeats[id].disabled = true;
reject( new Error( "Repeat Cancelled" ) );
};
});
repeats[id] = { cancel: cancelMe };
(function loop() {
let promises = [
module.exports.wait( delayMs / 1000 ),
cancellable
];
return Promise.race(
promises
).then( () => {
// Do not proceed if we got cancelled right when the wait finished. (race condition)
if ( !repeats[id].disabled ) {
let p = cb();
if ( !p || !p.then ) // In case the callback did not return a promise...
p = Promise.resolve();
if ( !repeats[id].disabled )
return p.then( () => {
// Do not repeat if we got cancelled while waiting for the callback
if ( !repeats[id].disabled )
return Promise.resolve().then( loop );
});
}
return null;
}).catch( err => {
// Ignore error if the repeat was cancelled voluntarily
if ( err.message != "Repeat Cancelled" )
return Promise.reject( err );
});
})();
return id;
};
exports.clearRepeat = function( repeatId ) {
repeats[repeatId].cancel();
};
exports.wait = function( seconds ) {
return new Promise( ( resolve ) => {
setTimeout( resolve, seconds * 1000 );
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment