Javascript Animation and Recursion - javascript

I have written a Javascript program that solves a puzzle game using a recessive technique.
That is, function solvePuzzle() calls function solvePuzzle() for a simpler puzzle until the solution is found. It alters the data in the board object.
I also have a function board.draw() that can display the state of the puzzle
It draws the initial game board as I expect and once I click on a button (triggering execution of solvePuzzle()) it draws the solved game board again as I expect.
However, I would like to show the intermediate puzzle states.
Initially, I inserted calls to board.draw() in the solvePuzzle() function but this does not do anything.
Researching Javascript animation has led me to create and execute this function
function animationLoop(timestamp) {
// 1 - Clear
ctx.clearRect(0, 0, c.width, c.height);
// 2 Draw
board.draw();
pieces.draw();
// call again mainloop after 16.6 ms (60 frames/s)
requestId = requestAnimationFrame(animationLoop);
}
requestId = requestAnimationFrame(animationLoop);
I am confident this is working as this only place I now call board.draw() and it show the initial state and switches to show the solved state after I press the solve button but... still no intermediate states are shown.
I then hypothesised the issue was that solution was so quick that it happens between frames but discounted this by placing this 'delay' in solvePuzzle
if (solutionCount%1000 == 0) {
confirm("Are you sure you wish to continue?");
};
I am now hypothesising solvePuzzle must run to completion before animationLoop can progress.
Is this hypothesis correct?
If so, how can I resolve my issue?
I am thinking I sort of need to continually end and resume my reclusive function at each state but cannot get my head around how I might do this.
Note: another reason I am confident the animation is working is that if I alter board from the console with say a statement like
board.layout[7].available = true;
the expected change is made to the display

JavaScript is single-threaded, and shares this thread with UI updates. Thus, when a function is started from top level, the browser does not do anything else until that function exits. This includes animation frame - animation is happening any time the page's thread is idle and an animation can be scheduled, but while your code is executing it can't.
If your calculation takes time, you need to split it into discrete pieces and let the browser breathe in between if you want UI updated (normally using setTimeout(f, 0), or inside requestAnimationFrame handler).
Another possibility is using Web Workers. They are a way to launch JavaScript in a separate thread. However, they cannot interact with the DOM at all, and can only communicate with messages. So, you can launch your calculation in a Web Worker, then from the worker periodically send messages to your main JS code in order to make it update the DOM in accordance to the results (both interim and final).
Thanks Alexander O'Mara and Kaiido for making me cover cases I forget.

Related

using requestanimationframe, profiling shows >80% time spent idle, still getting 4fps. why? (/how to fix?)

I have a simple canvas game. It's architected with a simple requestAnimationFrame loop that updates the state, and then renders to the canvas (simple!). It runs really well on desktop + iOS, but on chrome on android, it runs terribly.
Here's the loop, for example:
var tick = function()
{
requestAnimationFrame(tick,canvas);
cur_scene.tick();
cur_scene.draw();
}
So I pulled up the remote profiler, recorded a quick session, and over (any given) 1 second, it shows ~.82s idle (.11s scripting, .04s 'other', .02 painting, .005 rendering).
It also shows ~.1s per frame (the space between consecutive Animation Frame Fired entries on the flame graph). But the flame graph is like (consistent with the other measurements) 80% empty?
So I'm at a loss- what can I do to get this to render at a higher framerate? Am I reading the diagnostic info incorrectly? Have I structured the requestAnimationFrame loop incorrectly?
Edit: Here's an annotated picture of some of the performance diagnostics (picked up by recording a remote session on my android phone)
I think you could try to put the call to requestAnimationFrame() at the end of your method like this:
var tick = function()
{
cur_scene.tick();
cur_scene.draw();
requestAnimationFrame(tick,canvas);
}
Else you might end up entering the loop again without drawing anything but requesting the animation frame again.
When you check the following example, they also first do their changes to the object before they call requestAnimationFrame():
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
The above documentation explains this in the following text:
The method takes a callback as an argument to be invoked before the repaint. [...] You should call this method whenever you're ready to update your animation onscreen.

window.pageYOffset is sometimes 0 instead of a valid value

I'm having a problem where sometimes when my JavaScript in a Web page gets the value of window.pageYOffset it is inexplicably 0 even though I know the user is viewing the middle of the document and its value should be huge, like 650000. Note that a huge percentage of the time I get a reasonable value. But sometimes it's zero and sometimes it's a seemingly random small value, like in the 6000 range when I'm expecting 650000.
Rather than post a bunch of code, I'd like to ask some general questions to help me figure out where to begin to look.
This page is being displayed in an iOS WKWebView (though this problem can manifest in a similar context in an Android app). JavaScript methods in my app can be invoked in one of several ways:
When my app is notified that the page has finished loading (via a delegate method), it invokes a JavaScript method using evaluateJavaScript from the Objective-C code.
My app can call evaluateJavaScript at other times, not just when the page finishes loading.
A JavaScript function may be called as the result of a timer firing.
A JavaScript function may be called as the result of a scroll event.
I have been operating under the assumption that the JavaScript code on the page is always operating in a single thread. That is, I don't have a situation where a timer firing, a scroll event happening, or even a call from the Objective-C code (using evaluateJavaScript) is interrupting anything that might be happening in the JavaScript runtime. So I shouldn't have to worry about interrupting some system-level activity that is modifying window.pageYOffset while I'm trying to access it.
So that's my first question: Am I correct that someone outside my code is invoking my JavaScript methods on a single thread and not monkeying with the DOM on another thread?
My second question is related: My code modifies the DOM, adding and removing div elements. I've been assuming that those modifications are synchronous -- if I insert an element with insertAfter or insertBefore, I expect that the child/parent/sibling pointers are accurate upon return, and I assume that I can immediately access things like the top and left values on some other element and they will have been updated to reflect the inserted/removed element. The point being that I shouldn't have to "wait" for the DOM to "stabilize" after making changes and before checking something like window.pageYOffset. Is this correct?
One more clue: To help mitigate this, I have had good luck simply testing window.pageYOffset for zero at the top of a function. If it is zero, I call myself back on a timer (with just a 1 msec delay). If I do that long enough, it will eventually be non-zero.
Perhaps after reading all this, none of the detail is relevant and you know the answer to the basic question: Why do I sometimes get an invalid value (usually 0) in window.pageYOffset when the same line of code gives a valid value at other times.
The problem turned out to be that there appears to be a period of time between when I give the WKWebView a new HTML string to render and when it tells me that it is done loading the page that the existing page is still active. During this time, timers continue to fire, but some document and window properties will not be valid.
Because of the difficulty of debugging JavaScript running in this environment, I was tricking myself into thinking "eventually pageYOffset becomes valid" when in fact what I was seeing was that the new page eventually finished loading, and it was this new page that was generating valid calls to my timer functions.
In my particular case (may not work for everyone) I am able to detect the value of window.pageYOffset at the top of my timer function and if it is 0, call myself back after a brief delay. This allows me to handle the case where, for some reason, window.pageYOffset is just not yet valid (my test will eventually pass and my timer function will continue as usual) and the case where everything is in the process of being thrown away in favor of the new page (in which case the timer will not fire because the page goes away).

WinJS (WP8.1): update secondary tile from background task

I'm struggling with problem with my first winJS app. What I want to do is to update all secondary tiles created by my app in interval of a couple of hours when Internet connection is available. I've managed to do that from my app, using a simple "update" button, but I cannot do that from bg task. As for now I'm testing it on timezone change trigger not to wait half an hour for the result. I put simple console.log at the beginning of the bgtask to confirm that it occurs and it does, but none of the live tiles updates. What piece of information am I missing about updating secondary live tiles from bgtask?
Sounds like you've figured it out, but the key piece is the GetDeferral method on the background task instance. You need to call that and then call the Complete method on that only after all your task's work is done (including waiting for promises from any async calls to complete).

Can someone debug a progress bar issue in my Javascript applet?

Here is the applet
You can leave all the settings as they are, then hit "Calculate". After a few seconds, you should see some plots show up, then the progress bar below the "Calculate" button will fill up to 100%.
The problem is that I'd like the progress bar to increment while the code is running, not after it has completed as the plots are made. I know the code is long, but you can search for the following progress bar code:
setTimeout( update(count++, L.length, f.length, phi.length) );
It accesses the function update(s,x,y,z) which is defined directly before the main calculate() function.
I'm just confused as to why the progress bar doesn't update until all the processing is complete.
Thanks in advance!
The problem you observe is caused by javascript's single-threaded nature. Timeouts do not get executed while existing code is being executed. Web workers were introduced to deal with just that type of restriction. If you are unable to use web workers(e.g. due to IE limitations), using timeouts is the only way to emulate multythreading.
To check that, you may replace your line with update(count++, L.length, f.length, phi.length)() and change update to log values into console.
As for current update implementation, I suppose either there are some any mini timers inside jquery, causing the issue, or browser rendering capabilities are just not available for some reason:).
The reason is that your calculations and drawings take far less time than timeout. Therefore by the time timeout functions are executed, everything has already been drawn.
You could verify that by replacing your line with update(count++, L.length, f.length, phi.length)() - it will lead to instant update up to 100%.
To get progress bar work more precise you should review your calculation code and try rewriting it in a chunk-friendly manner. I mean having a possibility to execute calculations chunk by chunk and updating progress bar at the end of each chunk. Having that done, you could use timer to evaluate everything.
Sample:
(function(){
var arr = [1,2,3,4];
(function popLog(){
console.log(arr.pop());
if (arr.length > 0){
setTimeout(popLog, 100);
}
})();
})()
Another approach is using web workers, querying the status from time to time and updating progress bar appropriately. However, IE does not support them.

JavaScript's get-it-done nature

Is JavaScript intended to be running as little as possible on a website/webapp? By that I mean is the usual intention to run through all your js files as soon as the page loads and put them aside, and then when functions come up to execute them right away and be done with it?
I'm working on a project using google maps and I have a custom marker object scripted out, and a debugger has told me that the browser runs through all my js files before anything even appears on the page.
My problem comes in here: I wanted to animate certain markers to bounce up and down continuously with jQuery (similar to OS X icons in the dock) and my several attempts at infinite loop functions all just crash the browser. So I understand that the browser doesn't like that, but is there a way to have a simple script be repeating itself in the background while the user navigates the page? Or is JavaScript just not supposed to be used that way?
(I worked with Flash for a long time so my mindset is still there.)
Yes, Javascript functions should just do their bit and exit as soon as possible. The GUI and the scripts run on the same single thread, so as long as you are inside a Javascript function, nothing shows up in the browser. If you try to use an infinite loop, the browser will appear to freeze.
You use the window.setInterval and window.setTimeout methods to trigger code that runs at a specific time. By running an interval that updates something several times a second, you can create an animation.
You have to set a timer to execute a script after a defined time.
var timer = setTimeout(code, milliseconds);
will execute code in so-and-so milliseconds. Each execution of the script can set a new timer to execute the script again.
You can cancel a timed event using clearTimeout(timer).
Use setTimeout() or setInterval(). The MDC articles on it are pretty good.
You'll need to update inside of functions that run quickly, but get called many times, instead of updating inside of a loop.
Since you said that you are using jQuery, consider using its effects API (e.g., jQuery.animate()), it will make your life much easier!
Personally, I save as much code as possible for execution after the page has loaded, partly by putting all my <script>s at the bottom of <body>. This means a (perceived) reduction in page load time, whilst having all my JS ready to run when need be.
I wouldn't recommend going through everything you need to do at the beginning of the document. Instead, bind things to events such as clicks of buttons, etc.

Categories

Resources