How to detect when one function is complete from another function? - javascript

I have a javascript function that is being built to animate the collapse of a div, and then proceed with other jobs. The code is as follows:
function newsFeed() {
var self = this;
this.collapse = function(listingID,orig_height,curr_height,opacity) {
var listing = document.getElementById(listingID);
var reduceBy = 5;
if(curr_height > reduceBy) {
curr_height = curr_height-reduceBy;
listing.style.overflow = "hidden";
listing.style.height = (curr_height-40) + "px";
if(opacity > 0) {
opacity = opacity - 10;
var opaque = (opacity / 100);
listing.style.opacity=opaque;
listing.style.MozOpacity=opaque;
listing.style.filter='alpha(opacity='+opacity+')';
}
setTimeout(function() { self.collapse(listingID,orig_height,curr_height,opacity); },1);
}else{
return true;
}
}
this.remove = function(listingID) {
var listing = document.getElementById(listingID);
var currHeight = listing.offsetHeight;
if (this.collapse(listingID,currHeight,currHeight,100)) {
// DO SOME OTHER STUFF
}
}
}
var newsFeed = new newsFeed();
newsFeed.remove('closeMe');
I cannot get the this.remove function to wait while this.collapse finishes and returns true. Is this impossible? What is the best way to go on?
Important: I would like to be able to use this.collapse with other functions yet to be built in the same fashion as I do here.

I cannot get the this.remove function to wait while this.collapse finishes
That is correct, it is impossible to do so. In JavaScript there is a single flow of execution. When the browser calls your code you can do some processing, but for anything further to occur (timeouts or event calls) you must return control to the browser.
‘Asynchronous’ processes like collapse() are done by setting timeouts, so control must be returned to the browser many times; when remove() calls collapse() the first time it returns immediately after the first timeout is set; that timeout cannot be fired until remove() itself returns, so your 'if' code will only ever execute if the very first call to collapse() was the last frame of animation (ie. the element was 5px or smaller already). Otherwise collapse()'s ‘return true’ will just be returning true to the browser's timeout-caller, which doesn't care at all what value you return to it.
Some languages give you tools such as threads or coroutines that can allow an asynchronous routine to be run from a synchronous routine; JavaScript does not. Instead, remove() must supply collapse() with a callback function it can call itself on the last frame.

There is no way you can pause the execution in Javascript till something else happens. All you can do is attach a callback function to collapse to call after it is done executing the final step.
As a sidenote, jQuery provides functions like fade(), animate() etc and supports queuing. If you don't want to use jQuery, you can still look at the code to see how it's implemented.
See the examples in this page.

setTimeout is not a "sleep". The function will end right there and return "undefined".
To manage that, I think you should do something like:
var newsFeed = new newsFeed();
newsFeed.onaftercollapse = function () {
newsFeed.remove('closeMe'); // "newsFeed" or "self"? must test
};
And then instead of return true;, the collapse() will end with:
if (self.onaftercollapse) self.onaftercollapse();

This example demonstrates how to check if a function is complete.
function foo() {
foo.complete = false;
// your code here
foo.complete = true;
}
foo.complete = false;
if (foo.complete) { // foo execution complete
// your code here
}
This code demonstrates how to check if a function has been run once.
function foo() {
// your code here
foo.ranOnce || (foo.ranOnce = true);
}
foo.ranOnce = false;
if (foo.ranOnce) { // foo execution complete at least once
// your code here
}

Related

Simple but difficult. How to force external libs functions run sequentially?

If we have two function in javascript, one slow and one fast. For example:
function slow() {
setTimeout(function() {console.log("slow finished")}, 10000);
}
function fast() {
console.log("fast");
}
And these functions don't have inside of them new structures like promisses (if we do not implement after).
How can we force these functions run in order? For example:
function run() {
slow();
fast();
}
run();
How can we force fast wait slow finishes?
I'm looking a solution that could work inside mobile application browsers, becase of a Apache Cordova project of mine.
Is there a way to do this?
An idea of mine is inject a callback function between the functions.
And this callback is called at the end of the slow function, calling the fast function.
An important thing is I can't (or would not) rewrite the code of the slow and fast functions,
because they will reside inside external libraries.
I'm looking for a solution to countorn this problem as an external observer and manager.
How can we do this?
Edit
He I was a trying to solve the problem merging the answers. No success yet.
I had changed slow but this is not really allowed. I have changed it to se what is happening with a. I couldn't get something interesting because a becomes undefined immediately and not after slow finishes...
var a = "adsfadsfadsf";
function slow() {
setTimeout(function() {console.log("slow done"); console.log("a2", window.a);}, 3000);
}
function fast() {
console.log("a3", window.a);
console.log("fast done");
}
var newSlow = function() {
return new Promise(function(resolve, reject){
window.a = slow();
console.log("a", a);
resolve("Sucess");
});
};
newSlow().then(function(resolve){fast();}, function(reject){console.log("error");});
I have tried with resolve(slow()); no sucess too.
That's a very interesting question. Well I can think of a way where if it is changing some global variable "g" to some value say "true". In that case if you can run them sequentially as,
<script>
var g = false;
var i;
function slow() {
setTimeout(function() {console.log("slow finished");g=true;}, 10000);
}
function fast() {
console.log("fast");
}
function run() {
slow();
i = setInterval(function(){check();},1000);
}
function check(){
if(g){
fast();
clearInterval(i);
}
}
run();
</script>
As in this demo
UPDATE: Something just struck me and I guess we might be able to add a callback function to slow() even if we can't access it directly.
If a function is called without parenthesis then the entire function as a content is returned as a string so we can edit that string by adding fast() to it registering that string as a function using eval().
function run() {
var myFun = slow+"";
myFun = myFun.substring(0,myFun.length-1);
alert(myFun);
myFun += "fast();}";
//to register the string "myFun" as a function
eval(myFun);
slow();
}
So basically our slow() function becomes,
function slow(){
//function code
//the appended function
fast();
}
NOTE: This will not worked in the example given above where GarouDan has deliberately added setTimeout limit to recreate a scenario where the slow() function takes longer time than the fast() function. However, in a real-world scenario I'm sure this approach would definetly work.
You could use the Promise pattern.
Promises are tailor made for situations where various parts of code may run slow or fast or complete in unknowable amounts of time (or not complete at all), while still giving you execution control.
My personal favorite library that implements the Promise pattern is RSVP.
Here is some pseudocode to give you the idea. Run an operation that may take a long time, then run one only when the first has either completed, or handle it's failure.
function doFoo() {
var promise = new RSVP.Promise(function(resolve, reject) {
// do some long-running operation, like retrieve
// data from a slow site...
if (data.Status && data.Status === 200) {
resolve(data);
} else {
reject(data.Error)
}
});
return promise;
}
function doBar() {
var promise = new RSVP.Promise(function(resolve, reject) {
// do some fast operation, like count to 10
for (i = 0; i < 10; i++) {
console.log(i);
}
resolve("");
});
return promise;
}
Now you can call:
function inOrder() {
doFoo().then(function(success) {
doBar();
}).catch (function(failure) {
console.log("oops! " + failure);
}
});
}
This runs doFoo, and ONLY runs doBar after doFoo has completed successfully. Note that you could also run doBar even if doFoo has failed.

Javascript sleep/delay/wait function

Sorry if this question has already been asked here before, I could not find a suitable answer.
I am wanting to create a JavaScript sleep/delay/wait function that I can call anywhere in the script, like jQuery's .delay()
I am not able to use setTimeout, as I have a script that is generated by php, and so am not able to put it into two different functions, with the timeout in the middle. I need to create a function that allows me to do
alert("time started");
sleep(4000);
alert("time up");
I really do not want to use jQuery.
Here's a solution using the new async/await syntax.
async function testWait() {
alert('going to wait for 5 second');
await wait(5000);
alert('finally wait is over');
}
function wait(time) {
return new Promise(resolve => {
setTimeout(resolve, time);
});
}
Note: You can call function wait only in async functions
You cannot just put in a function to pause Javascript unfortunately.
You have to use setTimeout()
Example:
function startTimer () {
timer.start();
setTimeout(stopTimer,5000);
}
function stopTimer () {
timer.stop();
}
EDIT:
For your user generated countdown, it is just as simple.
HTML:
<input type="number" id="delay" min="1" max="5">
JS:
var delayInSeconds = parseInt(delay.value);
var delayInMilliseconds = delayInSeconds*1000;
function startTimer () {
timer.start();
setTimeout(stopTimer,delayInMilliseconds);
}
function stopTimer () {
timer.stop;
}
Now you simply need to add a trigger for startTimer(), such as onchange.
You will have to use a setTimeout so I see your issue as
I have a script that is generated by PHP, and so am not able to put it into two different functions
What prevents you from generating two functions in your script?
function fizz() {
var a;
a = 'buzz';
// sleep x desired
a = 'complete';
}
Could be rewritten as
function foo() {
var a; // variable raised so shared across functions below
function bar() { // consider this to be start of fizz
a = 'buzz';
setTimeout(baz, x); // start wait
} // code split here for timeout break
function baz() { // after wait
a = 'complete';
} // end of fizz
bar(); // start it
}
You'll notice that a inside baz starts as buzz when it is invoked and at the end of invocation, a inside foo will be "complete".
Basically, wrap everything in a function, move all variables up into that wrapping function such that the contained functions inherit them. Then, every time you encounter wait NUMBER seconds you echo a setTimeout, end the function and start a new function to pick up where you left off.
The behavior exact to the one specified by you is impossible in JS as implemented in current browsers. Sorry.
Well, you could in theory make a function with a loop where loop's end condition would be based on time, but this would hog your CPU, make browser unresponsive and would be extremely poor design. I refuse to even write an example for this ;)
Update: My answer got -1'd (unfairly), but I guess I could mention that in ES6 (which is not implemented in browsers yet, nor is it enabled in Node.js by default), it will be possible to write a asynchronous code in a synchronous fashion. You would need promises and generators for that.
You can use it today, for instance in Node.js with harmony flags, using Q.spawn(), see this blog post for example (last example there).
You can use this -
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
You could use the following code, it does a recursive call into the function in order to properly wait for the desired time.
function exportar(page,miliseconds,totalpages)
{
if (page <= totalpages)
{
nextpage = page + 1;
console.log('fnExcelReport('+ page +'); nextpage = '+ nextpage + '; miliseconds = '+ miliseconds + '; totalpages = '+ totalpages );
fnExcelReport(page);
setTimeout(function(){
exportar(nextpage,miliseconds,totalpages);
},miliseconds);
};
}

Weird infinite loop bug in jquery recursive function

I have a strange issue on the project I'm working with. This changes an image source and a content of a div automatically.
I have coded a function, but it falls into infinite loop and page does not load (page is showing the loading page always).
These are the codes:
$.fn.extend({
changehaber: function(a){
$('#cokokunanlarcontent').fadeOut('slow',function() {
$('.jquerycokokunanlarbutton').attr('src','images/sabah/buton-pasif.png');
$('img[rel="'+a+'"]').attr('src','images/sabah/buton-aktif.png');
}).html($('#'+a).html()).fadeIn('slow');
return this;
}
});
function slidecokokunanlar() {
$('#cokokunanlarcontent').html($('#cokokunanlar1').html()).delay(3000).changehaber('cokokunanlar2').delay(3000).changehaber('cokokunanlar3').delay(3000).changehaber('cokokunanlar4').delay(3000).changehaber('cokokunanlar5').delay(3000);
slidecokokunanlar();
}
slidecokokunanlar();
What's the issue here, when this is executed, I want the function to work infinitely, but the page shows it's always loading. This is the console's output:
Uncaught RangeError: Maximum call stack size exceeded
Thanks in advance
You can't call a function from inside itself without blocking up the whole execution stack. By calling the function from inside itself, you're effectively preventing it from ever returning, and as Javascript is single-threaded, everything will grind to a halt!
Change your function to this:
function slidecokokunanlar() {
$('#cokokunanlarcontent').html($('#cokokunanlar1').html()).delay(3000)...
setTimeout(slidecokokunanlar, 0);
}
This allows for concurrent execution without blocking the UI, thus allowing your page to remain responsive.
See this article on "chunking" for more information on how this works.
This is because JavaScript doesn't have proper tail calls.
Your function calls itself at the end of itself forever. The first one never finishes and returns, nor does the second, nor do any of them until you run out of stack and explode.
You might try using setTimeout instead. See an example on jsFiddle.
EDIT You might not want to use 0 unless you really need it to be running continuously. Even using 100, you'd execute the function 10 times per second.
function foo(){
console.log('foo');
setTimeout(foo, 0);
}
foo();
Here's a cleaner way to do it.
var coko = $('#cokokunanlarcontent'); // cokokunanlarcontent
var cokos = $('[id^="cokokunanlar"]').not(coko); // cokokunanlar1..2..3 etc
var total = cokos.length; // total quantity
var i = 0;
var allow = true;
$('.jquerycokokunanlarbutton').attr('src','images/sabah/buton-pasif.png');
function slidecokokunanlar( isRestart ) {
if( !isRestart ) {
$('img[rel="' + cokos[i].id + '"]').attr('src','images/sabah/buton-aktif.png');
coko.html( cokos.eq(i).html() )
.fadeIn( 'slow' );
}
if( allow ) {
coko.delay( 3000 )
.fadeOut('slow', function() {
i = (++i % total);
slidecokokunanlar(); // recursively call with next index or 0
});
}
}
slidecokokunanlar(); // start it off
function restartSlider() {
allow = true;
slidecokokunanlar( true );
}
function stopSlider() {
allow = false;
}
stopSlider(); // to stop it
restartSlider(); // to restart it

How to refactor the javascript code when sleep like functionality needed?

I see there are lot's of threads here in SO about asking for a javascript sleep function and I know it can be done only using setTimeout and setInterval.
I do some userscripting with greasemonkey and written a script that loads a lot of pages and calculates something from them. It works, but I don't want to request the pages too fast.
var html0=syncGet(url0); // custom function for sync ajax call.
// fill the something array
for(var i=0;i<something.length;i++)
{
// calculate url1,url2 using the array and the i variable
// do something with lots of local variables
var html1=syncGet(url1);
// I would put a sleep here.
// do something with the results
var html2=syncGet(url2);
// I would put a sleep here.
// do something with the results
// get url3 from the page loaded from url2
var html3=syncGet(url3);
// I would put a sleep here.
// do something with the results
}
// use the result of the for loop and lots of code will follow...
The actual code is a bit more complex and longer than this.
I'm crying for the nonexistent sleep function (and understand why is it not possible) How to refactor this to use setTimeout, setInterval functions and keep it readable (and working) too?
For example this:
var urls = ["your","u","r","l´s"];
var htmls = new Array(urls.length);
var time = 1000;
for(var i=0;i<urls.length;i++){
(function(i){
setTimeout(function(){
htmls[i] = syncGet(urls[i]);
if(i == urls.length-1){
//continue here
}
},time*i);
})(i);
}
I had a similar problem where a big loop was blocking the whole browser in some older browsers, I solved it using :
function handlenext(idx,length) {
idx++
//do your stuff here base on idx.
if (idx < length) {
setTimeout(function(){handlenext(idx,length)},1)
} else {
initSuccessEnd()
}
}
var ln = something.length;
if (ln>0) {
handlenext(0,ln);
} else {
initSuccessEnd()
}
here initSuccessEnd is a callback function called when all is finished ..
After a research I think Mozilla's new iterator-generator stuff could be the most apropriate. (It's supported since FF2)
function doSomething()
{
//.....
var html=syncGet(url1);
yield true;
var html2=syncGet(url2);
yield true;
var html3=syncGet(url3);
yield true;
//......
yield false;
}
function iteratorRunner(iterator,timeout)
{
if (iterator.next())
{
setTimeout(function(){iteratorRunner(iterator,timeout)},timeout);
}
else
{
iterator.close();
}
}
var iterator=doSomething(); // returns an iterator immediately
iteratorRunner(iterator,1000); // runs the iterator and sleeps 1 second on every yield.
I hope greasemonkey will handle that...

Sharing variable JavaScript

In following code, I am taking input
of an AJAX call into a function called
plr(). I want to detect when loading
is complete using the done variable.
But main thread is locking the
variable and the script hangs the
browser. If I put the alert in the
commented place, the purpose is
served. So, what other way can I use
to do the same?
function openX() {
LoadContentInto("Default.aspx", plr);
var obj = null;
done = false;
function plr() {
x = this.AJAXObject.responseText;
t = x.indexOf('{')
n = parseInt(x.substring(0, t));
s = x.substring(t, n + t);
p = eval('(' + s + ')');
obj = p;
done = true;
}
while (done != true)
{ // alert("hello");
}
alert(done);
}
Basically you have to make synchronous your ajax call, so there's no need to create an empty (blocking) while. the callback plr() will be executed on successful response, then remaining data will be called inside that callback
http://www.hunlock.com/blogs/Snippets:_Synchronous_AJAX
You should not wait that actively for the result. When the AJAX call is successfully finished you have the callback function called. In your case it seems that it is plr (although it is not clear what LoadContentInto exactly does).
It seems you have a temptation to make the AJAX success callback synchronous. Sometimes I used to have such passions, but so far it always showed up that there is an asynchronous way as well.
Maybe you want something like that:
function openX() {
LoadContentInto("Default.aspx", plr);
var obj = null;
var done = false; // you have your variable global! Make it local!
function plr() {
x = this.AJAXObject.responseText;
// ...
// put your code here
// ...
alert("Done!");
done = true;
}
setTimeout(function(){
if (!done) {
alert("Please wait!");
// Does the response and/or the operation after the responseText arives take a long time?
// Based on that decide how to inform the user
}
}, 100); // Set the timeout to right value.. based on your needs
}
Few comments to your code:
you have done declared as a global variable, it is very likely that it should be local
while (done != true) is much cleaner as while (!done)

Categories

Resources