How to pass event into a debounce function? - javascript

I am making a search on type Lightning Component for Salesforce.
I made a debounce function to check if a user stops typing, which does the delay successfully. However the function that runs in my debounce function will not accept an event now and a console.log(event) says 'undefined'. I am not sure how to fix this error. My code is below...
debounce(func, wait, immediate) {
var timeout;
return function executedFunction() {
var context = this;
var args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
termChange(evt) {
this.rows = [];
this.searchTerm = evt.target.value;
this.getCases();
}
handleTermChange = this.debounce(evt, function(){
this.termchange();
}, 2000, false)
When I used to just call termChange, It would search every key press and you would end up with duplicates, or unwanted records. Now with debounce it delays, but I can't find a way to pass the event in. (this.getCases() is another function I created that retrieves the records.) Any ideas on how to do this?

First, the debounce function takes a function as the first argument and you are trying to call it with event object. Second, termChange is camel cased but inside the debounce call is is all lowercase, so this code would not run anyways.
Now, let's take a close look at what debounce does. It takes a function and then returns another function that expects the same exact arguments. So if you just do:
handleTermChange = debounce(termChange)
then you should get a function that would take the event as it's first argument. To be safe, I would bind it to whatever "this" you use in your example and then it should be good to go

Related

Run a function inside an anonymous function inside event listener

Just working on debounce feature and found this piece of code that seems to be doing the trick:
$(document).ready(function() {
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
function searchUsers () {
// some code irrelevant to this question (...)
};
var myEfficientFn = debounce(searchUsers, 450);
document.getElementById("search").addEventListener('input', myEfficientFn);
});
Above seem to work well.
But I was curious if I can pass debounce function straight to addEventListener instead of saving it first in a variable myEfficentFn.
So I removed line var myEfficientFn = debounce(searchUsers, 450); from code and changed the last line to:
getElementById("search").addEventListener('input', function() {
debounce(searchUsers, 450);
});
but it stopped working. Why?
debounce is a function that, when called, returns another function, one which in your original code, is called when the event triggers:
var myEfficientFn = debounce(searchUsers, 450);
document.getElementById("search").addEventListener('input', myEfficientFn);
In contrast, in your second code, you're calling debounce inside the event listener. debounce returns a function, but you're never calling it: with
debounce(searchUsers, 450);
you have an unused function expression, kind of like having
const someVar = () => console.log('hi');
without ever using someVar later.
Pass the debounce call (which returns the function you want as the event listener) directly into addEventListener instead:
document.getElementById("search").addEventListener('input', debounce(searchUsers, 450));
The other answers left out a little bit of info that would help you understand what's happening:
getElementById("search").addEventListener('input', function() { // <--- this function gets called on click
debounce(searchUsers, 450); // this is a function, not a function call
});
getElementById("search").addEventListener('input', function(ev) { // <--- this function gets called on click
debounce(searchUsers, 450)(ev); // this is how you call the function returned by debounce
});
A short explanation;
The debounce function returns a function which is run by the event listener.
return function()
...
Your first approach saves the returned function to a variable and the even listener runs it.
addEventListener('input', myEfficientFn);
...
Your second approach gets the returned function within another function and no one really runs it.
debounce(searchUsers, 450); //WHo runs the returned function?
...
Solution in your own context - run the returned function!
getElementById("search").addEventListener('input', function(e) {
debounce(searchUsers, 450)(e);
});
you don't have to wrap it inside an anonymous function, you can simply use:
.addEventListener('input', debounce(searchUsers, 450))

How to trigger a function after settimeout function which is not in our control in ES6 or Javascript?

I need to trigger a custom JS function immediately after a function gets completed in other library Ex) CanvasJs core. As i dont have any control on canvas JS i could not able to achieve this. Please check the pseudo
function canvasFunction() { // I dont have any control to edit this function.
setTimeout(function(){
alert("After this call trigger my custom function.");
}, 3000);
}
function myCustomFunction() {
alert("Call this once above function completes.");
}
Original code:
$(window).on('resize.off-canvas', eventData, debounce(Drupal.offCanvas.resetSize, 100)).trigger('resize.off-canvas');
resetSize: function resetSize(event) {
// After this function is done, i need to call my custom function.
},
The original code triggers a "resize.off-canvas" event after debouncing the resetSize function for 100ms. You can listen for that event and delay, better yet, debounce the execution of your own function by the same duration with an arbitrary allowance.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
var myCustomFunction = debounce(function() {
console.log("Call this once after resize function completes and only once every 120ms.");
}, 120);
$(window).on('resize.off-canvas', myCustomFunction );
This is based on David Walsh's debounce implementation:
// https://davidwalsh.name/javascript-debounce-function
I haven't really tested it but I think it will work.
From what I understand from your question, you want to run myCustomFunction after the canvasFunction finishes. And since setTimeOut doesn't return a promise, your functions are not asynchronous (which is what we basically want to do).
To solve this, you can promisify the setTimeOut itself, with something like this:
`function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
};`
And then call it in your first function as await timeout(3000)
Then you can simply make your other function asynchronous as well and them it will execute in the order that you want.

Implementing a delay function using setTimeout() with passing optional arguments (Javascript)

I am trying to get to grips with Javascript by implementing intermediate functions from scratch. Currently trying to implement a delay function that executes an arbitrary function passed as an argument after a waiting time (wait). This also needs to be able to forward additionally passed arguments as extra arguments for the function being delayed.
What I have made so far isn't calling the function within the setTimeout(). Im not sure if its a syntax error or ive just missed the point completely. I have looked through similar questions on here and tried to implement some of the suggested results, however none seem to take the additional arguments aspect into consideration. Anyway, here is what I currently have.
var exampleDelay = function (func, wait) {
return function () {
setTimeout(func.apply(this, arguments), wait);
}
};
Any help tackling this would be appreciated (or if anyone can point me to an answer I may have missed).
Fran beat me to it but just for variety.
if you want to supply all the params at once this might be an option
var exampleDelay = function(callback,wait,args) {
var args = [].slice.call(arguments) // get the parent arguments and convert to an array
args.splice(0,2); // remove the first two argument which are the fuction supplied and the wait time
// a fuction to call the supplied function
var callnow = function() {
var params = arguments; // get the child arguments
var context = this;
setTimeout(function(){
callback.apply(context, params) // call the function
}, wait);
}
callnow.apply( this, args ) // use apply to supply the arguments extracted from the parrent
};
exampleDelay(console.log, 1000,"hey")
exampleDelay(console.log, 5,"hey", "there")
callnow.apply( this, args ) // we then call the function with apply and supply args extracted from the parent
well, you can handle function validation later to make sure that the first argument is a function
The function you return from exampleDelay is the one you call later one.
To preserve the arguments at the time of calling that function until the time the timer executes it you can wrap the intended function in an anonymous function within the timer. Then inside you can use apply to pass the previously stored arguments. Similar to the below.
var exampleDelay = function(func, wait) {
return function() {
var params = arguments;
var context = this;
setTimeout(function(){
func.apply(context, params)
}, wait);
}
};
var func1 = function(a, b) {
console.log(a + b);
}
var func2 = function(a, b, c) {
console.log(a, b, c);
}
var x = exampleDelay(func1, 1000);
var y = exampleDelay(func2, 2000);
x(12, 15);
y('How', 'Are', 'You?');

How to custom $watch in angular js?

I want to have a custom $watch. This watch I will make on a variable. I want when a the value of a variable is changed to see a message, and when the variable is not change his value to see another message.
An example is the next one: I have a textbox where I watch when a user writes something. I want to see a message when he writes something and when stop to see another message.
You can check with a timeout:
$scope.$watch("myVar",
function handleMyVarChange( newValue, oldValue ) {
//Change Display Message to Editing Message
//Timeout to checkif stop writing
$timeout(function (newValue) {
if(newValue === myVar) {
//Change Display Message to Not Editing Message
}
}, 100);
}
);
The concept I would recommend is called debounce in many javascript libraries. You give a function to call and a length of time that must pass without the function being called again before a callback is fired. Often times you can supply the option of firing the callback on the initial call as well. This would allow you to flip a switch on the first call, then flip it back once the amount of time has passed without the function being called again.
See: http://davidwalsh.name/javascript-debounce-function
Ex:
(a better example would create a debounce service using $timeout eliminating the need for $scope.$evalAsync())
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
var toggle = debounce(function() {
$scope.typing = !$scope.typing;
$scope.$evalAsync();
}, 500, true)
$scope.$watch('name', toggle)
});
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this;
var args = arguments;
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow){
func.apply(context, args);
}
function later() {
timeout = null;
func.apply(context, args);
}
};
}

How can I rate limit how fast a javascript function allows itself to be called?

I have a JavaScript function which actually ends up making a server-side call. I want to limit the rate at which this function can be called.
What is an easy way I can limit how fast my javascript function will get called by say 200-500 milliseconds or so? Should I use a javascript timer control?
Libraries like bottleneck and node-rate-limiter pretty much cover all use cases.
If your problem involves too much work being created, use a queue:
const work_queue = [];
function queue(message) {
work_queue.push(message)
}
function run() {
const work = work_queue.shift();
if (work !== undefined) {
scan_one(work);
}
}
setInterval(run, 15);
If you problem involves a function being called too often:
let last = +new Date();
function run() {
const now = +new Date();
if (now - last > 5000) { // 5 seconds
last = now;
run_once();
}
}
First you need to establish if you want to rate limit in that you disregard all function calls that are made during the period when you are waiting, or whether you want to simply queue up requests so that you ensure you never make more than X requests per second.
If you want the former solution (disregard new functional calls), then you should look at http://documentcloud.github.com/underscore/#throttle
If you want to rate limit so that you never call your function more than X times per second, but don't lose those function calls altogether, then you need a wholly different solution.
I have written an underscore extension at https://gist.github.com/1084831
You can see a working example at http://jsbin.com/upadif/8/edit#preview
This will not allow the function to run if less than 500 milliseconds have passed since the last call.
(function(window, undefined){
var canCall = true;
window.funcName = function(){
if (!canCall)
return;
//Your function
canCall = false;
setTimeout(function(){
canCall = true;
}, 500);
}
})(window);
You can create a flag that is raised when the function is called and start a timer and if this flag is raised then you can not call the function, then after a certain time, the timer is called and he turns off the flag, allowing you to call the function again.
The flag can be anything, like a bool or something.
It kind of depends what functionality you want. Here is a link to a page that has 2 great functions: https://remysharp.com/2010/07/21/throttling-function-calls
throttle: process first call, then throttle next calls based on a threshhold (first and last call will be processed, but only a couple calls in between)
debounce: don't process any calls until function hasn't been called for a delay (only 1 will be called after a call and quite period)
It depends on what you want to do with subsequent calls, where you wanna run it etc.
Wait on subsequent calls: throttle-wait
Discard subsequent calls lodash.throttle sugar.throttle...
More advance like throttle on multiple server or more configs
bottleneck
Also serverside throttler like ratelimiter (async-ratelimiter), node-rate-limiter
You can also use the SugarJS function "throttle":
http://sugarjs.com/api/Function/throttle
I would suggest Pat Migliaccio solution found here
function limiter(fn, wait){
let isCalled = false,
calls = [];
let caller = function(){
if (calls.length && !isCalled){
isCalled = true;
calls.shift().call();
setTimeout(function(){
isCalled = false;
caller();
}, wait);
}
};
return function(){
calls.push(fn.bind(this, ...arguments));
caller();
};
}
You can easily test it by creating a loop:
const logMessageLimited = limiter(msg => { console.log(msg); }, 1000);
for (let i = 0; i < 10; i++){
logMessageLimited(`[Message Log] Action (${i}) rate limited.`);
}
You can use debounce function
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this,
args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
var logging = debounce(function(){
alert("Heavy task");
}, 5000);
setTimeout(logging, 100);//callback executed after 5 seconds
More information on how debounce function works here http://qnimate.com/javascript-limit-function-call-rate/
try setinterval( "function()", 500)
fooCanBeCalled = true;
function foo(){
if(!fooCanBeCalled) return;
//Whatever you want to do
fooCanBeCalled = false;
setTimeout(function(){
{
fooCanBecalled = true;
}
, delayInMilliseconds);
}

Categories