I have a function that I want to subscribe it to a event queue. When it executes this function I need it to unsubscribe from the queue.
functionToExecute is the function I want to execute.
onDOMReady subscribes the attachDOMHandler function which in turn executes functionToExecute and unsubscribes itself. I have:
onDOMReady : function(functionToExecute){
subscribe(this.attachDOMHandler(functionToExecute));
},
attachDOMHandler : function(functionToExecute) {
unsubscribe(this.attachDOMHandler(functionToExecute));
functionToExecute();
}
Everything works fine when I have the following:
onDOMReady : function(functionToExecute){
subscribe(functionToExecute, this, true);
},
However I need the function to unsubscribe after it is executed. My plan was to use the attachDOMHandler function which executes the function and has the unsubscribe behaviour.
When I run the former I get a "Uncaught RangeError: Maximum call stack size exceeded" in the Chrome console. It looks like it gets stuck in an infinite loop which makes sense since I keep running attachDOMHandler in the unsubscribe which gets caught in a loop.
I basically need to subscribe a function that I specify by parameter passing. When the function is executed it unsubscribes.
Hope I have explained this ok.
You don't want to call the function directly:
function f() { // the function to subscribe unsubscribes itself
// and calls the actual function
unsubscribe(f);
functionToExecute();
}
subscribe(f);
Related
I am learning javascript. I have a doubt. I heard that when we use the setTimeout method, javascript will remove the callback function of that setTimeout from the call stack and put it on another call stack and continue executing the current call stack.
function f1() {
console.log("print first");
setTimeout(() => {
console.log("print in time out");
}, 4000);
console.log("print last");
}
The output of this function
print first
print last
print in time out
Because the callback of setTimeOut placed into a new stack.
What if I use Ajax request or something like that takes some time to execute without using async. Something like this.
function API(){
const apiCALL = fetch('https://jsonplaceholder.typicode.com/posts');
console.log(apiCALL);
}
is this also put into another call stack?
One thing you have to know it's that both fetch and setTimeout functions are not really a functions of javascript. They belong to Window - Web Apis. You can read more about it here: https://developer.mozilla.org/en-US/docs/Web/API/Window
And whenever you are using those functions they go of the call stack and into a window api that is written in probably in C# or something. Once they are done doing what they are suppose to do, they send back response inside a callback queue, which u can imagine as a different call stack.
Javascript itself is single threaded but with functions from window api it can be run async.
If I call the function once like this
var button = document.querySelector('button');
button.addEventListener('click', once);
function once() {
console.log('one');
button.removeEventListener('click', once);
}
It's calling only once.
But if I called like this once()
var button = document.querySelector('button');
button.addEventListener('click', once());
function once() {
console.log('one');
button.removeEventListener('click', once());
}
Exception throws
Exception: InternalError: too much recursion
Could you please explain why this is happening.
() after function name invoke's the function. So as button.addEventListener('click', once()); you are bind the return value of once() method which is undefined.
And since once() is called recursively without any break statement, you are getting the InternalError: too much recursion.
You should pass the function reference.
button.addEventListener('click', once);
For Additional Info:
Pointer Vs Delegates
If you put () after the name of a variable holding a function, then you call the function.
If a function calls itself, then it will get called, call itself, call itself again and so on unto infinity. This is recursion. Doing something unto infinity will cause a computer to run out of memory, so it is undesirable.
JavaScript engines prevent this by throwing an exception when you try it.
(There are exceptions, of course, a function that calls itself conditionally can be very useful).
The first code is correct, because you register the function to be called.
The second code tries to register the result of the function call once(). This means you actually execute the function when you only want to register it. Now, in your function body, you do the same to deregister the callback. Here again, you call the function you are already executing and hence, you recurse infinitely.
Edit: I apologize for any confusion. In my code, a loop is running in the script indefinitely until a certain condition is reached. My question is, if an event listener calls a function while this loop is running, after the completion of the function, where would execution continue?
Well, that's pretty much self-explanatory, I guess. After a function is called by an event listener, where does execution of code continue after the function finishes?
There is an event queue, and whatever is next in that queue gets executed. It could for example be a mouse click event, a window resize or a time-out. If there is nothing on the queue, the container, in which JavaScript runs, will just loop until there is something in the queue to process.
You can read more in MDN's article on "Concurrency model and Event Loop":
A JavaScript runtime contains a message queue, which is a list of messages to be processed. To each message is associated a function. When the stack is empty, a message is taken out of the queue and processed. The processing consists of calling the associated function (and thus creating an initial stack frame). The message processing ends when the stack becomes empty again.
A common way to put something on the message queue is by calling
setTimeout(myfunction, 0);
As the delay in the second argument is 0, the function myfunction will be called once the currently executing code completes, i.e. the call stack becomes empty. Note however, that it could be that some other events were already on the queue. In that case those will still be executed first.
What with a long-lasting loop?
You added the following to your question:
In my code, a loop is running in the script indefinitely until a certain condition is reached. My question is, if an event listener calls a function while this loop is running, after the completion of the function, where would execution continue?
If your loop does not run via asynchronous calls (e.g. with repeated calls to setTimeout or with setInterval), but looks like this:
while (!endingCondition) {
// do nothing
}
then there is no way another (JavaScript) event listener gets called. Such an event will be waiting on the message queue. Only when your current loop and any code after it finishes, will that event be processed and result in the call of the event listener.
So let's look at the following example:
var clicked = false;
document.onclick = function() {
clicked = true;
}
while (!clicked) {};
alert('clicked!');
Here one might hope that the while loop gets interrupted by a click and shows the alert, but that is not true. The click event will be in the operating system's message queue, but will not be consumed by JavaScript because it always first completes the code it is currently executing. As stated in the above-mentioned article on MDN:
whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs.
That any code includes all JavaScript code, including code in event handlers.
How to write "interruptible" code
The above example can be made to work like this:
var clicked = false;
document.onclick = function() {
clicked = true;
}
function detectClick() {
if (!clicked) {
setTimeout(detectClick, 0); // keep repeating
return;
}
alert('clicked!');
// do something more ...
};
detectClick(); // call for first time
// We get here immediately. Code will end, and events will be processed.
// One of those "events" is a time-out, to which we have set a handler: detectClick.
// At some point a click event will be there as well, triggering the other handler.
I have some javascript functions being called on Document Ready:
fogFields();
getLoS();
getShips();
startGame();
getNextMove();
However, it is as though getNextMove() is being called first, most likely as all it does is an ajax call and alerts the result. All the other functions have more work, so, the first thing that happens on load is the getNextMove() alert, and in the background you can see that none of the other functions did their work. Until I click OK on the alert window, no results are shown. Can I make it so that until a function finishes, the next wont even start. Some functions call their own extra functions before they finish, and that works in order, but I cant do that with the whole code...
Given the code in your question, there is no way the call to getNextMove can be invoked before startGame has been exited, regardless of their contents.
It may be true that a function that has been scheduled asynchronously (via timeout, AJAX callback etc.) within startGame completes at any time before or after the invocation of getNextMove, but this is a separate issue. To resolve that issue we need to know more about the contents of the functions.
If the other functions have an AJAX call in them, then these AJAX calls most certainly take a callback argument, which is a function that gets executes, when the AJAX call has finshed. Now, if you want to execute your functions in a way, the one function starts when the AJAX call of the previous function finished, you can add an additional callback argument to your own functions, which will then be passed to the AJAX calls. This should illustrate what I mean:
function logFields(callback) {
ajaxCall(callback);
}
function getLoS(callback) {
ajaxCall(callback);
}
function getShips(callback) {
ajaxCall(callback);
}
function startGame(callback) {
ajaxCall(callback);
}
function getNextMove() {
}
fogFields(function(){
getLoS(function(){
getShips(function(){
startGame(function(){
getNextMove();
});
});
});
});
If all of your functions use a ajax call then just use promises.
Simply return it, for example:
function fogFields(){
return $.ajax();
};
and then just use .then:
fogFields().then(getLos());
more information about deffered object on jquery doc page if you use it.
Or implementation in pure javascript you can find here and more theory here.
or another option, which I will not recommend you is to set async param in $.ajax call to false. Again it's for case you use jQuery.
I have this code where I am using two javascript functions that do some manipulating for me. One function does some calculation but calls the other function before doing anything else. When the other function returns to it, it is supposed to do the final calculations.
The problem: The call to the other function is not executing properly. Even before the second function returns the first function executes completely.
code:
firstfunction{
secondfunction();
do something more nothing related to second
}
secondfunction(){
setTimeout(func1(){
do something independently
then call func1 depending on some condition
},time);
}
second function is used somewhere else also and is working fine.
My question:
I used this code thinking that the first function will not execute before second function executes completely. Is it right? Isn't this the way javascript functions should run? The first function executes completely before second returns. I am sure of this because after second returns the position for the graphic first is supposed to place that graphic on screen. But the first executes completely and the graphic is placed awkwardly on screen and viewer can see it moving to right position given by the loop of second. Is setTimeout causing this problem?
Please help.
This happens because you use setTimeout. setTimeout will make it asynchronous. Execution of second function will be completed after setting the interval and execution-flow will go back to first function. So you have to split your first function and make a third function which will have the final steps. This third function has to be invoked from the setTimeout handler.
function firstfunction(){
secondfunction(params);
}
function secondfunction(params){
setTimeout(func1(){
do something independently
then call thirdfunction depending on some condition
},time);
}
function thirdfunction(params){
do something more nothing related to second
}
In this method you have to pass everything as parameters from one function to other.
You can also do this in a different way by making third one a callback function. This way you can make everything in the scope of firstfunction available for thirdfunction.
function firstfunction{
secondfunction(thirdfunction);
function thirdfunction(){
do something more nothing related to second
}
}
function secondfunction(callback){
setTimeout(func1(){
do something independently
then call callback depending on some condition
},time);
}
"Even before the second function returns the first function executes completely."
No, in fact the second function returns immediately after it calls setTimeout() - it does not wait for the timeout to occur. The func1() function that you pass to setTimeout() will be called later after the specified timeout delay, but meanwhile execution continues with whatever follows the setTimeout() call (in this case the end of the function follows, so it returns to the first function and then the rest of the first function continues). To put it another way, setTimeout() does not pause execution.
If you need something to happen after the timeout you need to either place it in the function you pass to setTimeout() or call it from that function.