So I recently decided to make an idle game from AngularJS, but after a few days of coding it I realized a problem.
The $interval function slows down a huge amount when not actively looking at the screen.
Searching on Google and stackoverflow took me to web workers, and one post actually had me figure out how to make a factory of a web worker to implement into my controller, but I was never able to figure out how to make them run functions (or if this is even possible).
myApp.factory("interval", ['$q',
function($q) {
var blobURL = URL.createObjectURL(new Blob([
'function addInterval() {' +
'i = "add()";' +
'postMessage(i);' +
'setTimeout("addInterval()", 10);' +
'}' +
'addInterval();' +
'function saveInterval() {' +
'x = "save()";' +
'postMessage(x);' +
'setTimeout("saveInterval()", 30000);' +
'}' +
'saveInterval();'
], {
type: 'application/javascript'
}));
var worker = new Worker(blobURL);
var defer = $q.defer();
worker.addEventListener('message', function(e) {
defer.resolve(e.data);
}, false);
return {
doWork: function(myData) {
defer = $q.defer();
worker.postMessage(myData); // Send data to our worker.
return defer.promise;
}
};
}
]);
I practically want to know if it's possible to have the web workers run my save() and interval() functions that are in the controller. I tried to console.log(defer.resolve(e.data)), but it returned undefined.
...and I literally have no clue what the deferring/resolving/promising even does.
Thanks!
So after Scott corrected me about how web workers function I decided to set the interval from 10ms to 1000ms...and was defeated...
...but then I got the great idea of "How about instead of making it one fixed rate, I'll cancel the interval upon page/tab blur (not focussed) and change the intervals + divisions accordingly!". This is what I came up with that solved my problems and made the game continue to run - even when not viewing it.
$scope.division = 100;
var x = $interval(add, 10);
angular.element($window).bind('focus', function() {
$scope.division = 100;
$interval.cancel(x);
x = $interval(add, 10);
}).bind('blur', function() {
$scope.division = 1;
$interval.cancel(x);
x = $interval(add, 1000);
});
Related
In school we use this coding website called code.org. It's pretty handy and seems to be able to do anything that normal coding software can, just a bit more digestible for beginners such as myself. I'm asking a question that I'm not sure is even possible to answer. In the game I'm trying to figure out how to add cps (cookies per second) to the counter. My issue is that this could be done in a loop, but other things need to happen outside of the loop. So I'm not sure how to add them, but still be able to run other bits of code without it getting stuck in a loop. The code will be posted at the bottom. This project is just for fun and I do not intend to pass this work off as my own. Thanks for the help!
(please note that this IS the entirety of the code i have so far...)
var cookies = 0;
var incriment = 1;
var cps = 0;
var autoClickers = 0;
var autoClickerCost = 1;
var autoClickerAdd = 0.50;
var upgradeClickCost = 100;
setText("upgradeClickCostText","cost: "+ upgradeClickCost);
setText("autoClickerCostText", "cost: " + autoClickerCost);
onEvent("image1", "click", function() {
cookies = cookies + incriment;
console.log("you have: "+cookies+" cookies");
setText("cookieNumber", "Cookies: " + cookies);
});
onEvent("upgradeClick", "click", function() {
if(cookies >= upgradeClickCost){
cookies = cookies - upgradeClickCost;
console.log("you have: "+cookies+" cookies");
setText("cookieNumber", "Cookies: " + cookies);
incriment = incriment * 2;
upgradeClickCost = upgradeClickCost * 2;
setText("upgradeClickCostText", "cost: "+ upgradeClickCost);
}
});
onEvent("shopScrnBtn", "click", function() {
setScreen("shop_screen");
console.log("went to shop!");
});
onEvent("gameScrnBtn", "click", function() {
setScreen("game_screen");
console.log("went to cookie!");
});
function addCookies(){
cookies = cookies + cps;
}
onEvent("buyAutoClicker", "click", function() {
if(cookies >= autoClickerCost){
cookies = cookies - autoClickerCost;
autoClickers++;
console.log("you have: "+cookies+" cookies");
setText("cookieNumber", "Cookies: " + cookies);
autoClickerAdd = autoClickerAdd * autoClickers;
cps = cps + autoClickerAdd;
}
console.log("auto clicker purchased");
});
(also note that this code snippet does not work properly as you won't be on code.org or have the proper buttons to handle the events.)
The feature you are looking for is probably setInterval which runs a function every n milliseconds.
function runAutoClicker() {
cookies = cookies + cps;
}
// Run auto-clicker every second (every 1000 milliseconds)
setInterval(runAutoClicker, 1000);
I'm not seeing any loops here, just click events. Am I missing something? If there was a loop, we could see what's inside vs. what's out. Typically you handle variables changes (and not changing them) within loops with conditional if statements.
Having decided to implement all of my Javascript libraries as modules compatible with node.js, I've now put together a simple 'require' function to be used on the client side of things. I do realize that there are already plenty of very good implementations out there of this, but most seem fairly heavy-handed so I'm just opting to go with the "roll-your-own" approach.
Here's my 'require.js' file:
/*
A client-side script loading class designed to be compatible with node.js
*/
function require(filename)
{
try
{
var
script = null,
ajax = new XMLHttpRequest(),
index = filename.toLowerCase().indexOf('.js');
if(index <= 0 || index != filename.length - 3)
filename += '.js';
ajax.onload = function()
{
script = this.responseText;
};
ajax.open('GET', filename, false);
ajax.send();
return _isolate_script_(script);
}
catch(error)
{
return null;
}
}
function _isolate_script_(_script_)
{
return (new Function
(
'var exports = {}, module = {exports : null}; '
+ _script_ +
'; return module.exports || exports;'
)).call();
}
Example module ('example.js'):
/*
Example of a multiple-class export file
*/
exports.metallic = function(value)
{
return (Math.sqrt(4 + value * value) + value) / 2;
}
exports.inverse_metallic = function(value)
{
return exports.metallic(-value);
}
Example of a module consumer ('main.js'):
function main()
{
var
example = require('example');
if(example != null)
{
var
value = Math.floor(Math.random() * 100) + 1;
alert
(
'example.metallic(' + value + ') = ' + example.metallic(value)
+ '\n' +
'example.inverse_metallic(' + value + ') = ' + example.inverse_metallic(value)
);
}
else
alert('Error: cannot load "example"');
}
Finally, a basic HTML file that invokes our page logic:
<!DOCTYPE html>
<html>
<head>
<script src = 'require.js'></script>
<script src = 'main.js'></script>
</head>
<body onload = "main()"></body>
</html>
So my specific question is simply whether or not I've implemented everything correctly, and moreover if the framework and use-cases seem kosher enough?
It seems that you are trying to reimplement Browserify:
Browsers don't have the require method defined, but Node.js does. With Browserify you can write code that uses require in the same way that you would use it in Node.
If you want to implement a similar thing yourself then take a look at the Browserify source code.
See: https://github.com/substack/node-browserify
I'm just going to answer this myself.
The main issue is that older browsers sometimes freeze-up with synchronous ajax requests. Simply launching the entire 'main' function in it's own thread seems to be a good solution for that. That is, if some unforeseen issue arises with loading scripts then the page itself may not be responsive, but the browser should at least be unaffected. In other words, something along the lines of this:
<!DOCTYPE html>
<html>
<head>
<script src = 'require.js'></script>
<script src = 'main.js'></script>
</head>
<body onload = "setInterval(main)"></body>
</html>
As to concerns about throughput, I've now tested this with huge amounts of data (~100 MB) and it appears to make little difference on performance as to whether the code was loaded with a script tag or via the ajax call. All major browsers on two different operating systems and the results were pretty much the same. So unless I see convincing evidence to the contrary I'm just going to assume this to be the rule rather than the exception.
All that said, I'm certainly still open to any comments or criticisms that may be.
If you want module caching as well try this:
let parent = "";
let cache = {};
/**
* Load a JavaScript text synchronously.
*
* #param url The url.
*/
function cludge(url) {
/* resolve and check */
if (parent !== "")
url = new URL(url, parent).href;
let map = cache[url];
if (map !== undefined)
return map;
map = {};
cache[url] = map;
/* load and execute */
let back = parent;
try {
parent = url;
let request = new XMLHttpRequest();
request.open('GET', url, false);
request.send();
let fun = new Function("exports", "require", request.responseText);
fun(map, cludge);
} finally {
parent = back;
}
return map;
}
But the name resolution is not as elaborate as in nodejs.
I'm working on this code :
JS
<script src='file://C:\blablabla\JavaScript\bignumber.js-master\bignumber.js'></script>
<script>
document.write("<h1>\"blabla\"</h1>\n<h3>blabla</h3>");
function problem(){
var img = document.getElementById('problemi');
return img.style.display = img.style.display === 'block' ? 'none' : 'block';
}
function problem551(){
problem();
var t0 = performance.now();
var max = 1e+15;
var sum = new BigNumber(1);
for(var i=1;i<max;i++)
sum = sum.plus(scomponi(sum,0));
var t1 = performance.now();
document.getElementById("p551").innerHTML = 'blabla<span>'+max+"</span> blabla <span>" + sum +"</span> in <span>"+(t1 - t0)/1000+"</span> blaaa";
}
function scomponi(num,sum){
var str=num.toString();
for(var i = 0 ; i< str.length ;i++ ){
sum += parseInt(str[i]);
}
return sum;
}
</script>
HTML
<body>
<div>
<button onclick="problem551()" >PROBLEM 551</button>
<img id="problemi" src="PROBLEM551.png" style="display: none;">
<p id="p551"></p>
</div>
</body>
But Chrome crashes, it gives me this :
How can prevent this error on my function, he has a loop from 1 to 1e+15, so it takes too much time. I read something about WEB WORKERS but is unclared for me. I want to use it on my function problem551(), so someone can explain me how it works?
Move your functions to a new file, funcs.js:
And use new Worker("funcs.js") to start it.
You can use postMessage to send the result back as described in MDN:
Sending messages to and from a dedicated worker
The magic of workers happens via the postMessage() method and the
onmessage event handler. When you want to send a message to the
worker, you post messages to it like this (main.js):
first.onchange = function() {
myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');
}
second.onchange = function() {
myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');
}
So here we have two elements represented by the variables
first and second; when the value of either is changed,
myWorker.postMessage([first.value,second.value]) is used to send the
value inside both to the worker, as an array. You can send pretty much
anything you like in the message.
In the worker, we can respond when the message is received by writing
an event handler block like this (worker.js):
onmessage = function(e) {
console.log('Message received from main script');
var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
console.log('Posting message back to main script');
postMessage(workerResult);
}
Do expect it to still take a lot of time, it is quite a long operation, but it will hopefully prevent Chrome from bringing up the error.
I see you are trying to solve Project Euler problem 551.
You are throwing away a valuable piece of information:
You are given a_10^6 = 31054319.
You don't need to begin your iterations from 1. The next number can any arbitary number in the sequence.
a_10^6 = 31054319
a_(10^6 + 1) = 31054319 + 3 + 1 + 0 + 5 + 4 + 3 + 1 + 9
I am writing a service in AngularJS. I need to be able to test this service. I have everything working, except for one piece. Currently, the function that I want to test is defined like this:
return {
triggerError: false,
watchers: [],
createWatcher : function (options) {
var defer = $q.defer();
var watchId = Math.floor((Math.random() * 1000000) + 1);
var delay = 10000;
if (options && options.milliseconds) {
delay = options.milliseconds;
}
this.watchers.push($interval(
function() {
if (this.triggerError) {
defer.reject('There was an error watching.');
}
// Generate a random number
var randomA = Math.floor((Math.random() * 50) + 1);
var randomB = Math.floor((Math.random() * 50) + 1);
var result = { a: randomA, b: randomB };
defer.notify(result);
},
delay
));
return {
watchId: watchId,
promise: defer.promise
};
}
}
As the name implies, this function creates a watcher that will fire on an interval. The reason that it is written like this is because I need to be consistent with another framework that's in use. For that reason, I need to be able to make the following call in my code:
var watcher = myService.createWatcher({...});
watcher.promise.then(
function() { ... },
function(err) {},
function(result) {
console.log('A: ' + result.a);
console.log('B: ' + result.b);
}
)
I need to be able to write a test that will wait for the watcher to fire 10 times. Currently, I have the following:
it('should fire ten times', function(done) {
var count = 0;
var watch = myService.creationWatcher({});
watch.promise.then(
function() { console.log('here 1'); },
function(err) { console.log('here 2'); },
function(result) {
count = count + 1;
console.log(result);
}
);
interval.flush(5000);
rootScope.$digest();
});
I see it print 'here 1'. However, I'm not sure if I've written my test incorrectly, or if I'm missing something in my service. In my opinion, my service looks correct. However, I'm unsure about the test itself. Can someone please tell me what I'm doing wrong? I can't figure out why my test won't wait for the watcher to fire 10 times.
Thank you!
Your delay in the test is set too low. When you use
interval.flush(100000)
instead it should work - although it looks like your code will not run as-is, I had to make some adjustments.
I've created a fiddle to show you it works with a higher delay (with some small fixes). If you have questions like this in the future, including code in the question but also a fiddle will greatly help others helping you :-)
BTW: I'm assuming the creationWatcher() in your testing code was a typo and the actual function to be called should be createWatcher().
i'm developing a phonegap app using a lot of javascript. Now i'm debugging it using Safari Developer Tool, in particular i'm focused on some button that on the device seems to be a bit luggy.
So I've added some console.timeEnd() to better understand where the code slow down, but the "problem" is that when i open the console the code start running faster without lag, if i close it again, the lag is back.
Maybe my question is silly but i can't figure it out
Thanks
EDIT: Added the code
function scriviNumeroTastiera(tasto){
console.time('Funzione ScriviNumeroTastiera');
contenutoInput = document.getElementById('artInserito').value;
if ($('#cursoreImg').css('display') == 'none'){
//$('#cursoreImg').show();
}
else if (tasto == 'cancella'){
//alert(contenutoInput.length);
if (contenutoInput.length == 0) {
}
else {
indicePerTaglioStringa = (contenutoInput.length)-1;
contenutoInput = contenutoInput.substr(0, indicePerTaglioStringa);
$('#artInserito').val(contenutoInput);
//alert('tastoCanc');
margineAttualeImg = $('#cursoreImg').css('margin-left');
indicePerTaglioStringa = margineAttualeImg.indexOf('p');
margineAttualeImg = margineAttualeImg.substr(0, indicePerTaglioStringa);
margineAggiornato = parseInt(margineAttualeImg)-20;
$('#cursoreImg').css('margin-left', margineAggiornato+'px');
}
}
else {
//contenutoInput = document.getElementById('artInserito').value;
contenutoAggiornato = contenutoInput+tasto;
margineAttualeImg = $('#cursoreImg').css('margin-left');
indicePerTaglioStringa = margineAttualeImg.indexOf('p');
margineAttualeImg = margineAttualeImg.substr(0, indicePerTaglioStringa);
margineAggiornato = parseInt(margineAttualeImg)+20;
$('#cursoreImg').css('margin-left', margineAggiornato+'px');
$('#artInserito').val(contenutoAggiornato);
}
console.timeEnd('Funzione ScriviNumeroTastiera');
}
The code is a bit crappy, but it's just a beginning ;)
This could happen because PhoneGap/Cordova creates its own console object (in cordova.js), and it gets overwritten when you open the Safari console (safari's might be faster than phonegap's, that could be why you notice it faster).
So, one way to measure the time properly, without opening the console, would be to go to the good old alert, so you'd first add this code anywhere in your app:
var TIMER = {
start: function(name, reset){
if(!name) { return; }
var time = new Date().getTime();
if(!TIMER.stimeCounters) { TIMER.stimeCounters = {} };
var key = "KEY" + name.toString();
if(!reset && TIMER.stimeCounters[key]) { return; }
TIMER.stimeCounters[key] = time;
},
end: function(name){
var time = new Date().getTime();
if(!TIMER.stimeCounters) { return; }
var key = "KEY" + name.toString();
var timeCounter = TIMER.stimeCounters[key];
if(timeCounter) {
var diff = time - timeCounter;
var label = name + ": " + diff + "ms";
console.info(label);
delete TIMER.stimeCounters[key];
}
return diff;
}
};
(This just mimics the console.time and console.timeEnd methods, but it returns the value so we can alert it).
Then, instead of calling:
console.time('Funzione ScriviNumeroTastiera');
you'd call:
TIMER.start('Funzione ScriviNumeroTastiera');
and instead of calling:
console.timeEnd('Funzione ScriviNumeroTastiera');
you'd call:
var timeScriviNumeroTastiera = TIMER.end('Funzione ScriviNumeroTastiera');
alert('Ellapsed time: ' + timeScriviNumeroTastiera);
This would give you the proper ellapsed time without opening the console, so it computes the real time in the phonegap app.
Hope this helps.
Cheers
This really isn't something you would normally expect - opening the console should not speed up anything. If anything, it will make things slower because of additional debugging hooks and status display. However, I've had a case like that myself. The reason turned out to be very simple: opening the console makes the displayed portion of the website smaller and the code efficiency was largely dependent on the viewport size. So if I am right, making the browser window smaller should have the same effect as opening the console.