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?');
Related
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
I'm trying to understand this example code, what is the function of line 15, why start(timeout)? (Sorry, I'm new to programming)
var schedule = function (timeout, callbackfunction) {
return {
start: function () {
setTimeout(callbackfunction, timeout)
}
};
};
(function () {
var timeout = 1000; // 1 second
var count = 0;
schedule(timeout, function doStuff() {
console.log(++count);
schedule(timeout, doStuff);
}).start(timeout);
})();
// "timeout" and "count" variables
// do not exist in this scope.
...why start(timeout)?
In that example, there's actually no reason for passing timeout into start, since start doesn't accept or use any arguments. The call may as well be .start().
What's happening is that schedule returns an object the schedule function creates, and one of the properties on that object is called start, which is a function. When start is called, it sets up a timed callback via setTimeout using the original timeout passed into schedule and the callback function passed into schedule.
The code calling schedule turns around and immediately calls the start function on the object it creates.
In the comments, Pointy points out (well, he would, wouldn't he?) that the callback function is calling schedule but not doing anything with the returned object, which is pointless — schedule doesn't do anything other than create and return the object, so not using the returned object makes the call pointless.
Here's the code with those two issues addressed:
var schedule = function (timeout, callbackfunction) {
return {
start: function () {
setTimeout(callbackfunction, timeout)
}
};
};
(function () {
var timeout = 1000; // 1 second
var count = 0;
schedule(timeout, function doStuff() {
console.log(++count);
schedule(timeout, doStuff).start(); // <== Change here
}).start(); // <== And here
})();
It's not very good code, though, frankly, even with the fixes. There's no particularly good reason for creating a new object every time, and frankly if the book is meant to be teaching, this example could be a lot clearer. Inline named function expressions and calls to methods on objects returned by a function...absolutely fine, but not for teaching. Still, I don't know the context, so those comments come with a grain of salt.
Here's a reworked version of using the schedule function by reusing the object it returns, and being clear about what bit is happening when:
(function () {
var timeout = 1000; // 1 second
var count = 0;
// Create the schedule object
var scheduleObject = schedule(timeout, doStuff);
// Set up the first timed callback
scheduleObject.start();
// This is called by each timed callback
function doStuff() {
// Show the count
console.log(++count);
// Set up the next timed callback
scheduleObject.start();
}
})();
The function schedule is executed as a function. That function returns an object. Like you can see with the { start... }. With the returned object it calls out the start function. This is called chaining. So the start function is executed after is set the function.
What is strange is that the timeout is passed to the start function which has no parameters.
var cancel = setTimeout(function(){clearTimeout(cancel);}, 500);
var cancel = setTimeout(clearTimeout(cancel), 500);
Scholastic question: The first of these two expressions work, while the second does not. The setTimeout() method is accepting a function and a duration as its arguments and both of these examples are clearly providing that. The only difference is that the first is a function definition while the second is a function invocation.
If functions designed to take a function as an argument can only handle function definitions, how do you go about providing that function with the variables it may need? For example:
stop = function(y){clearInterval(y)};
count = function(x){
var t = 0,
cancel = setInterval(function(){console.log(++t);},1000);
setTimeout(stop(cancel),x);
};
count(5000);
The function above doesn't work because it's invoking the function
stop = function(){clearInterval(cancel)};
count = function(x){
var t = 0,
cancel = setInterval(function(){console.log(++t);},1000);
setTimeout(stop,x);
};
count(5000);
The function above doesn't work because the stop() doesn't have access to the cancel variable.
Thank you in advance for attempting to educate me on the work-around for this type of issue.
The setTimeout() method is accepting a function and a duration as its
arguments and both of these examples are clearly providing that. The
only difference is that the first is a function definition while the
second is a function invocation.
Yes but when you invoke a function you return the result which could be a string, integer, etc..., so you are no longer passing a function pointer but some string, integer, ... which is not what the setTimeout function expects as first argument.
Think of the second example like this:
var result = clearTimeout(cancel); // result is now an integer
setTimeout(result, 500); // invalid because setTimeout expects a function pointer
If functions designed to take a function as an argument can only
handle function definitions, how do you go about providing that
function with the variables it may need?
You could use closures:
var stop = function(y) { clearInterval(y); };
var count = function(x) {
var t = 0,
var cancel = setInterval(function() { console.log(++t); }, 1000);
setTimeout(function() { stop(cancel); }, x);
};
count(5000);
or simply:
var count = function(x) {
var t = 0,
var cancel = setInterval(function() { console.log(++t); }, 1000);
setTimeout(function() { clearInterval(cancel); }, x);
};
count(5000);
You get around it exactly as you have in the first line of code by wrapping the function call with an anonymous function.
Try passing in the cancel variable to the anonymous function.
stop = function(cancel){clearInterval(cancel)};
count = function(x){
var t = 0,
cancel = setInterval(function(){console.log(++t);},1000);
setTimeout(stop(cancel),x);
};
count(5000);
Local variables are always injected into nested scopes, for example those introduced by function declarations via function () { }. This is what is commonly called a closure and it forms an important tool in Javascript programming.
Therefore, setTimeout( function() { stop(cancel); },x); will do, the inner function has access to the cancel variable defined in the outer scope (it can even change its value).
If I have an arbitrary function myFunc, what I'm aiming to do is replace this function with a wrapped call that runs code before and after it executes, e.g.
// note: psuedo-javascript
var beforeExecute = function() { ... }
var afterExecute = function() { ... }
myFunc = wrap(myFunc, beforeExecute, afterExecute);
However, I don't have an implementation of the required wrap function. Is there anything that already exists in jQuery like this (I've had a good look through the docs but cannot see anything)? Alternatively does anybody know of a good implementation of this because I suspect that there are a bunch of edge cases that I'll miss if I try to write it myself?
(BTW - the reason for this is to do some automatic instrumentation of functions because we do a lot of work on closed devices where Javascript profilers etc. are not available. If there's a better way than this then I'd appreciate answers along those lines too.)
Here’s a wrap function which will call the before and after functions with the exact same arguments and, if supplied, the same value for this:
var wrap = function (functionToWrap, before, after, thisObject) {
return function () {
var args = Array.prototype.slice.call(arguments),
result;
if (before) before.apply(thisObject || this, args);
result = functionToWrap.apply(thisObject || this, args);
if (after) after.apply(thisObject || this, args);
return result;
};
};
myFunc = wrap(myFunc, beforeExecute, afterExecute);
The accepted implementation does not provide an option to call wrapped (original) function conditionally.
Here is a better way to wrap and unwrap a method:
/*
Replaces sMethodName method of oContext with a function which calls the wrapper
with it's list of parameters prepended by a reference to wrapped (original) function.
This provides convenience of allowing conditional calls of the
original function within the wrapper,
unlike a common implementation that supplies "before" and "after"
cross cutting concerns as two separate methods.
wrap() stores a reference to original (unwrapped) function for
subsequent unwrap() calls.
Example:
=========================================
var o = {
test: function(sText) { return sText; }
}
wrap('test', o, function(fOriginal, sText) {
return 'before ' + fOriginal(sText) + ' after';
});
o.test('mytext') // returns: "before mytext after"
unwrap('test', o);
o.test('mytext') // returns: "mytext"
=========================================
*/
function wrap(sMethodName, oContext, fWrapper, oWrapperContext) {
var fOriginal = oContext[sMethodName];
oContext[sMethodName] = function () {
var a = Array.prototype.slice.call(arguments);
a.unshift(fOriginal.bind(oContext));
return fWrapper.apply(oWrapperContext || oContext, a);
};
oContext[sMethodName].unwrapped = fOriginal;
};
/*
Reverts method sMethodName of oContext to reference original function,
the way it was before wrap() call
*/
function unwrap(sMethodName, oContext) {
if (typeof oContext[sMethodName] == 'function') {
oContext[sMethodName] = oContext[sMethodName].unwrapped;
}
};
This is the example I would use
<script type="text/javascript">
var before = function(){alert("before")};
var after = function(param){alert(param)};
var wrap = function(func, wrap_before, wrap_after){
wrap_before.call();
func.call();
wrap_after.call();
};
wrap(function(){alert("in the middle");},before,function(){after("after")});
</script>
You could do something like:
var wrap = function(func, pre, post)
{
return function()
{
var callee = arguments.callee;
var args = arguments;
pre();
func.apply(callee, args);
post();
};
};
This would allow you to do:
var someFunc = function(arg1, arg2)
{
console.log(arg1);
console.log(arg2);
};
someFunc = wrap(
someFunc,
function() { console.log("pre"); },
function() { console.log("post"); });
someFunc("Hello", 27);
Which gives me an output in Firebug of:
pre
Hello
27
post
The important part when wrapping this way, is passing your arguments from the new function back to the original function.
Maybe I'm wrong, but I think you can directly create an anonym function and assign it to myFunc:
myFunc = function(){
BeforeFunction();
myFunc();
AfterFunction();
}
In this way you can control the arguments of every function.
I have a Javascript object that requires 2 calls out to an external server to build its contents and do anything meaningful. The object is built such that instantiating an instance of it will automatically make these 2 calls. The 2 calls share a common callback function that operates on the returned data and then calls another method. The problem is that the next method should not be called until both methods return. Here is the code as I have implemented it currently:
foo.bar.Object = function() {
this.currentCallbacks = 0;
this.expectedCallbacks = 2;
this.function1 = function() {
// do stuff
var me = this;
foo.bar.sendRequest(new RequestObject, function(resp) {
me.commonCallback(resp);
});
};
this.function2 = function() {
// do stuff
var me = this;
foo.bar.sendRequest(new RequestObject, function(resp) {
me.commonCallback(resp);
});
};
this.commonCallback = function(resp) {
this.currentCallbacks++;
// do stuff
if (this.currentCallbacks == this.expectedCallbacks) {
// call new method
}
};
this.function1();
this.function2();
}
As you can see, I am forcing the object to continue after both calls have returned using a simple counter to validate they have both returned. This works but seems like a really poor implementation. I have only worked with Javascript for a few weeks now and am wondering if there is a better method for doing the same thing that I have yet to stumble upon.
Thanks for any and all help.
Unless you're willing to serialize the AJAX there is no other way that I can think of to do what you're proposing. That being said, I think what you have is fairly good, but you might want to clean up the structure a bit to not litter the object you're creating with initialization data.
Here is a function that might help you:
function gate(fn, number_of_calls_before_opening) {
return function() {
arguments.callee._call_count = (arguments.callee._call_count || 0) + 1;
if (arguments.callee._call_count >= number_of_calls_before_opening)
fn.apply(null, arguments);
};
}
This function is what's known as a higher-order function - a function that takes functions as arguments. This particular function returns a function that calls the passed function when it has been called number_of_calls_before_opening times. For example:
var f = gate(function(arg) { alert(arg); }, 2);
f('hello');
f('world'); // An alert will popup for this call.
You could make use of this as your callback method:
foo.bar = function() {
var callback = gate(this.method, 2);
sendAjax(new Request(), callback);
sendAjax(new Request(), callback);
}
The second callback, whichever it is will ensure that method is called. But this leads to another problem: the gate function calls the passed function without any context, meaning this will refer to the global object, not the object that you are constructing. There are several ways to get around this: You can either close-over this by aliasing it to me or self. Or you can create another higher order function that does just that.
Here's what the first case would look like:
foo.bar = function() {
var me = this;
var callback = gate(function(a,b,c) { me.method(a,b,c); }, 2);
sendAjax(new Request(), callback);
sendAjax(new Request(), callback);
}
In the latter case, the other higher order function would be something like the following:
function bind_context(context, fn) {
return function() {
return fn.apply(context, arguments);
};
}
This function returns a function that calls the passed function in the passed context. An example of it would be as follows:
var obj = {};
var func = function(name) { this.name = name; };
var method = bind_context(obj, func);
method('Your Name!');
alert(obj.name); // Your Name!
To put it in perspective, your code would look as follows:
foo.bar = function() {
var callback = gate(bind_context(this, this.method), 2);
sendAjax(new Request(), callback);
sendAjax(new Request(), callback);
}
In any case, once you've made these refactorings you will have cleared up the object being constructed of all its members that are only needed for initialization.
I can add that Underscore.js has a nice little helper for this:
Creates a version of the function that will only be run after first
being called count times. Useful for grouping asynchronous responses,
where you want to be sure that all the async calls have finished,
before proceeding.
_.after(count, function)
The code for _after (as-of version 1.5.0):
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
The license info (as-of version 1.5.0)
There is barely another way than to have this counter. Another option would be to use an object {} and add a key for every request and remove it if finished. This way you would know immediately which has returned. But the solution stays the same.
You can change the code a little bit. If it is like in your example that you only need to call another function inside of commonCallback (I called it otherFunction) than you don't need the commonCallback. In order to save the context you did use closures already. Instead of
foo.bar.sendRequest(new RequestObject, function(resp) {
me.commonCallback(resp);
});
you could do it this way
foo.bar.sendRequest(new RequestObject, function(resp) {
--me.expectedCallbacks || me.otherFunction(resp);
});
That's some good stuff Mr. Kyle.
To put it a bit simpler, I usually use a Start and a Done function.
-The Start function takes a list of functions that will be executed.
-The Done function gets called by the callbacks of your functions that you passed to the start method.
-Additionally, you can pass a function, or list of functions to the done method that will be executed when the last callback completes.
The declarations look like this.
var PendingRequests = 0;
function Start(Requests) {
PendingRequests = Requests.length;
for (var i = 0; i < Requests.length; i++)
Requests[i]();
};
//Called when async responses complete.
function Done(CompletedEvents) {
PendingRequests--;
if (PendingRequests == 0) {
for (var i = 0; i < CompletedEvents.length; i++)
CompletedEvents[i]();
}
}
Here's a simple example using the google maps api.
//Variables
var originAddress = "*Some address/zip code here*"; //Location A
var formattedAddress; //Formatted address of Location B
var distance; //Distance between A and B
var location; //Location B
//This is the start function above. Passing an array of two functions defined below.
Start(new Array(GetPlaceDetails, GetDistances));
//This function makes a request to get detailed information on a place.
//Then callsback with the **GetPlaceDetailsComplete** function
function GetPlaceDetails() {
var request = {
reference: location.reference //Google maps reference id
};
var PlacesService = new google.maps.places.PlacesService(Map);
PlacesService.getDetails(request, GetPlaceDetailsComplete);
}
function GetPlaceDetailsComplete(place, status) {
if (status == google.maps.places.PlacesServiceStatus.OK) {
formattedAddress = place.formatted_address;
Done(new Array(PrintDetails));
}
}
function GetDistances() {
distService = new google.maps.DistanceMatrixService();
distService.getDistanceMatrix(
{
origins: originAddress,
destinations: [location.geometry.location], //Location contains lat and lng
travelMode: google.maps.TravelMode.DRIVING,
unitSystem: google.maps.UnitSystem.IMPERIAL,
avoidHighways: false,
avoidTolls: false
}, GetDistancesComplete);
}
function GetDistancesComplete(results, status) {
if (status == google.maps.DistanceMatrixStatus.OK) {
distance = results[0].distance.text;
Done(new Array(PrintDetails));
}
}
function PrintDetails() {
alert(*Whatever you feel like printing.*);
}
So in a nutshell, what we're doing here is
-Passing an array of functions to the Start function
-The Start function calls the functions in the array and sets the number of PendingRequests
-In the callbacks for our pending requests, we call the Done function
-The Done function takes an array of functions
-The Done function decrements the PendingRequests counter
-If their are no more pending requests, we call the functions passed to the Done function
That's a simple, but practicle example of sychronizing web calls. I tried to use an example of something that's widely used, so I went with the Google maps api. I hope someone finds this useful.
Another way would be to have a sync point thanks to a timer. It is not beautiful, but it has the advantage of not having to add the call to the next function inside the callback.
Here the function execute_jobs is the entry point. it take a list of data to execute simultaneously. It first sets the number of jobs to wait to the size of the list. Then it set a timer to test for the end condition (the number falling down to 0). And finally it sends a job for each data. Each job decrease the number of awaited jobs by one.
It would look like something like that:
var g_numJobs = 0;
function async_task(data) {
//
// ... execute the task on the data ...
//
// Decrease the number of jobs left to execute.
--g_numJobs;
}
function execute_jobs(list) {
// Set the number of jobs we want to wait for.
g_numJobs = list.length;
// Set the timer (test every 50ms).
var timer = setInterval(function() {
if(g_numJobs == 0) {
clearInterval(timer);
do_next_action();
}
}, 50);
// Send the jobs.
for(var i = 0; i < list.length; ++i) {
async_task(list[i]));
}
}
To improve this code you can do a Job and JobList classes. The Job would execute a callback and decrease the number of pending jobs, while the JobList would aggregate the timer and call the callback to the next action once the jobs are finished.
I shared the same frustration. As I chained more asynchronous calls, it became a callback hell. So, I came up with my own solution. I'm sure there are similar solutions out there, but I wanted to create something very simple and easy to use. Asynq is a script that I wrote to chain asynchronous tasks. So to run f2 after f1, you can do:
asynq.run(f1, f2)
You can chain as many functions as you want. You can also specify parameters or run a series of tasks on elements in an array too. I hope this library can solve your issues or similar issues others are having.