1
\$\begingroup\$

Implementation to accommodate different behaviours based on the frequency a user interface element is clicked. It supports number of clicks and number of clicks in a time frame.

var thresholder = (function() {
    
    return { supply : (betweenShifts) => {
        
        return {
            
            millis : () => { return {
            
                or : (alternativeFunction) => {
                    
                    var millisSinceShift = Date.now();

                    return (shiftFunction) => {
                        
                        if ( Date.now() - millisSinceShift > betweenShifts ) {
                            millisSinceShift = Date.now();
                            shiftFunction();
                            return;
                        }
                        millisSinceShift = Date.now();
                        setTimeout( () => { alternativeFunction(); }, 1);
                    };
                    
                }
            } }
            , taps : () => {
                    
                    return { inMillis : (millisBetweenTaps) => {
                        
                        return { or : (alternativeFunction) => {
                            
                            var counter = (function(from) {
                                var tapCounter = from;
                                
                                return { incremented : () => { return ++tapCounter; } 
                                       , decremented : () => { return --tapCounter; }
                                       , counted : () => { return tapCounter; }
                                       , reset : () => { tapCounter = from; }
                                       };
                            })(1);
                            var previousShiftMillis = Date.now();
                            
                            return (shiftFunction) => {
                                    if ( Date.now() - previousShiftMillis >= millisBetweenTaps ) { 
                                        previousShiftMillis = Date.now();
                                        counter.reset();
                                    }
                                    
                                    if ( counter.counted() > betweenShifts ) {
                                        return;
                                    }
                                    
                                    if ( counter.incremented() == betweenShifts ) {
                                        setTimeout( () => { alternativeFunction(); }, 1);
                                        return;
                                    }

                                    shiftFunction();
                            };
                        } };
                    }
                };
            }
        };
    } };
} )();

var secondsBetweenShifts = 2;
var millisBetweenShifts = secondsBetweenShifts * 1000;
var alternative = () => { /* on click / on tap alternative code */ };
var tapper = thresholder.supply(3).taps().inMillis(millisBetweenShifts)
                        .or( alternative );
var shift = thresholder.supply(millisBetweenShifts).millis()
                       .or( alternative );

document.getElementById(“an-id”).addListener(“click”, () => { tapper( () => { shift( () => { /* on click / on tap function to call */ } ); } );  } );

Looking forward for any feedback so to have something to argue about. All the feedback is welcomed though of most interest is feedback from the functional programming perspective and whether there is a well established terminology to refer this type of implementation.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

JavaScript isn't my language, but I'm always somewhat concerned when I see multiple accesses to the current time in close succession like this:

if ( Date.now() - millisSinceShift > betweenShifts ) {
    millisSinceShift = Date.now();
    shiftFunction();
    return;
}
millisSinceShift = Date.now();
setTimeout( () => { alternativeFunction(); }, 1);

It appears that those three Date.now() are intended to refer to the same time-point, so I would call it just once:

thisEventTime = Date.now();
if ( thisEventTime - millisSinceShift > betweenShifts ) {
    millisSinceShift = thisEventTime;
    shiftFunction();
    return;
}
millisSinceShift = thisEventTime;
setTimeout( () => { alternativeFunction(); }, 1);

In this particular case (and the similar one further down), there's unlikely to be much elapsed time between the calls to now(), but using a single call makes it clear to readers without having to closely inspect every intervening action.

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.