Today I have tried to write a simple prophiler utility for javascript functions.
The problem is that the more interaction I launch the more the system slows down so
I think that at some point I have a memory leak leading to this problem.
The experiment's code follows.
//Benchmark prototype
var Prophiler = (function( ){
var _benchMark = (function( func, duration, callBack ){
var _interval = 1000;
var _startMark = new Date().getTime();
var _now = _startMark;
var _count = 0;
var _rounds = 0;
var _throttle = (function( ){
while( ( _now - _startMark ) < _interval ){
func( );
_now = new Date().getTime( );
_count++;
}
_startMark = new Date().getTime();
_rounds++;
if( _rounds <= ( duration ) ){
window.setTimeout( _throttle, 25 );
return false;
}
else{
var _res = {};
_res.elapsedTime = duration;
_res.executions = _count;
_res.averageTime = _res.elapsedTime / _res.executions;
_res.averageTimeMs = _res.averageTime * 1000;
callBack( _res );
return false;
}
});
_throttle( );
});
return{
getProphile : function( params ){
_benchMark( params.subject, params.duration, params.callBack );
}
}
})( );
//Test
var sumNum = function( param1, param2 ){
var y = param1;
for( var i = 0; i < param2; i++ ){
y += param2;
}
};
Prophiler.getProphile({
subject: function( ){
sumNum( 10, 30 );
},
duration: 5,
callBack: function( data ){
console.log( data );
}
});
Just for the sake of comparison between the module pattern and a prototypal pattern. The performance improvement is more noticeable in Chrome but can be also noticed in firefox.
function BenchMark(fn, duration, callback){
this.fn = fn;
this.duration = duration;
this.callback = callback;
this.rounds = 0;
this.count = 0
this.timerId = null;
this.startMark = (new Date()).getTime();
this._execute = this.scope(this, 'execute');
this._execute();
}
BenchMark.prototype.scope = function(context, method){
return function(){
context[method].call(context);
}
}
BenchMark.prototype.execute = function(){
if(this.timerId !== null){
clearTimeout(this.timerId);
}
var count = 0, now = this.startMark;
while( (now - this.startMark) < 1000){
this.fn();
now = (new Date()).getTime();
count++;
}
this.startMark = (new Date()).getTime();
this.count += count;
this.rounds++;
if(this.rounds <= this.duration){
this.timerId = setTimeout(this._execute, 25);
return false;
}else{
var averageTime = this.duration / this.count;
this.callback({
elapsedTime : this.duration,
executions : this.count,
averageTime : averageTime,
averageTimeMs : averageTime * 1000
});
return false;
}
}
function Profiler(){
}
Profiler.prototype.benchmark = function(fn, duration, callback){
new BenchMark(fn, duration, callback);
}
function sumNum( param1, param2 ){
var y = param1;
for( var i = 0; i < param2; i++ ){
y += param2;
}
};
var profiler = new Profiler();
var profilerCalled = 0;
var intervalId = setInterval(function(){
console.log('Profiler: ', profilerCalled+1)
profiler.benchmark(function(){
sumNum(10, 30)
}, 5, function(result){
console.log(result)
});
profilerCalled++;
if(profilerCalled == 10){
clearInterval(intervalId);
console.log('stopped')
}
}, 10000);
In the end I found the error.
It was an algo misconception.
Obiviously each call adds a 25ms delay that get loosed since _startMark = new Date().getTime(); is called after comparison!
Now it works quite well!
Related
Is it possible to transform this code to generate when the page loads instead of when the button is clicked. I tried to change the events to load but it does not function. I do not know what I exactly I am doing wrong.
<p><button id="generate">Generate</button></p>
<p><code id="output"></code></p>
(function() {
function IDGenerator() {
this.length = 8;
this.timestamp = +new Date;
var _getRandomInt = function( min, max ) {
return Math.floor( Math.random() * ( max - min + 1 ) ) + min;
}
this.generate = function() {
var ts = this.timestamp.toString();
var parts = ts.split( "" ).reverse();
var id = "";
for( var i = 0; i < this.length; ++i ) {
var index = _getRandomInt( 0, parts.length - 1 );
id += parts[index];
}
return id;
}
}
document.addEventListener( "DOMContentLoaded", function() {
var btn = document.querySelector( "#generate" ),
output = document.querySelector( "#output" );
btn.addEventListener( "click", function() {
var generator = new IDGenerator();
output.innerHTML = generator.generate();
}, false);
});
})();
There is small bug.
Just change the document object to window like below
window.addEventListener('DOMContentLoaded', function() {
});
How to change images with different time interval in a single div using CSS3, JavaScript or jQuery. At the last time interval should be cleared.
You can try this http://codepen.io/jammer99/pen/PNErKp
var timing = [1000, 2000, 500, 300, 800],
timeouts
function runinterval() {
timeouts = setTimeout(function() {
clearTimeout(timeouts);
timing.shift();
$(".imgholder").css("background-image", "url(https://unsplash.it/100/100/?random&i=" + new Date().getTime() + ")")
if (timing.length != 0)
runinterval();
}, timing[0])
}
runinterval();
Maybe something like this can help you.
var setTimer = (function(){
function setTimer( options ){
this.timings = options.timings;
this.element = options.element;
this.images = options.images;
this.index = -1;
this.interval = false;
this.init();
}
setTimer.prototype.init = function(){
this.image_ = document.createElement('img');
this.image_.setAttribute('class','slider-image');
this.element.appendChild( this.image_ );
this.set();
};
setTimer.prototype.set = function(){
if( this.interval && false !== this.interval ){
clearTimeout( this.interval );
}
if( this.index >= this.images.length-1 ){ this.index = 0; }
else{ this.index++; }
var timing = this.timings[this.index];
console.log(this.index);
console.log(timing);
this.interval = (function(_this){
return setTimeout(function(){
_this.switch_image();
},timing);
})(this);
};
setTimer.prototype.switch_image = function(){
var index = this.index;
console.log('switching image to '+this.images[index]);
this.image_.setAttribute('src',this.images[index]);
this.set();
}
return setTimer;
})();
setTimeout(function(){
var options = {
timings: [10,1000,2000],
images : [ 'url1','url2','url3'],
element: document.getElementById('your-image-container-id')
};
new setTimer(options);
},1000);
Thanks for seeing my question.
I am using wp-pro-quiz plugin for quiz. I want to know that how can I pause the timer if the window is not in focus or is blur and resume it when it is back to focus.?
My code:
I get reset when it get focused
var timelimit = (function () {
var _counter = config.timelimit;
var _intervalId = 0;
var instance = {};
instance.stop = function () {
if (_counter) {
window.clearInterval(_intervalId);
globalElements.timelimit.hide();
}
};
instance.start = function () {
var x;
var beforeTime;
if (!_counter)
return;
var $timeText = globalElements.timelimit.find('span').text(plugin.methode.parseTime(_counter));
var $timeDiv = globalElements.timelimit.find('.wpProQuiz_progress');
globalElements.timelimit.show();
$.winFocus(function (event) {
console.log("Blur\t\t", event);
},
function (event) {
console.log("Focus\t\t", event);
x = _counter * 1000;
beforeTime = +new Date();
});
_intervalId = window.setInterval(function () {
var diff = (+new Date() - beforeTime);
var elapsedTime = x - diff;
if (diff >= 500) {
$timeText.text(plugin.methode.parseTime(Math.ceil(elapsedTime / 1000)));
}
$timeDiv.css('width', (elapsedTime / x * 100) + '%');
if (elapsedTime <= 0) {
instance.stop();
plugin.methode.finishQuiz(true);
}
}, 16);
};
return instance;
})();
Use this wrapper function to pause, resume your timeout.
var Timer;
Timer = function(callback, delay) {
var remaining, start, timerId;
timerId = void 0;
start = void 0;
remaining = delay;
this.pause = function() {
window.clearTimeout(timerId);
remaining -= new Date - start;
};
this.resume = function() {
start = new Date;
window.clearTimeout(timerId);
timerId = window.setTimeout(callback, remaining);
};
this.resume();
};
Intialize it like this, timer = new Timer("callback_function_here", 45000)
In this case total time is 45 seconds for the callback and upon event triggers(blur or focus in your case) it will pause or resume the timer accordingly.
timer.pause() //pause the timer
timer.resume() //resume the timer
P.S - Use this function as per the logic of your code. You will have to make the timer calls accordingly in your code
I did it this way:
var time0 ; var setTimeout_Int; var focused = true; var resume_Fun ;
var addTime =0; var addTimeDiff =0;
window.onfocus = function() {
focused = true;
var d = new Date();
addTimeDiff = addTimeDiff +( d.getTime() - addTime );
resume_Fun();
};
window.onblur = function()
{
focused = false;
};
function init()
{
var d = new Date();
time0 = d.getTime();
setTimeout_Int = setTimeout(update, 1000 )
}
function update()
{
clearTimeout(setTimeout_Int);
var d = new Date();
if(focused)
{
if(d.getTime() -(time0+addTimeDiff) < 20000)
{
setTimeout_Int= setTimeout(update, 1000 )
}
}
else
{
addTime = d.getTime();
resume_Fun = update;
}
}
init();
Hey I am using a Spawn function I found for a javascript game I am creating to randomy generate enemies to click on. But it is very slow and I would like it to be able to generate faster and faster.. is it possible to modify this code in that way? the spawn function is at the end of the code.
function Gem(Class, Value, MaxTTL) {
this.Class = Class;
this.Value = Value;
this.MaxTTL = MaxTTL;
};
var gems = new Array();
gems[0] = new Gem('green', 10, 0.5);
gems[1] = new Gem('blue', 20, 0.4);
gems[2] = new Gem('red', 50, 0.6);
function Click(event)
{
if(event.preventDefault) event.preventDefault();
if (event.stopPropagation) event.stopPropagation();
else event.cancelBubble = true;
var target = event.target || event.srcElement;
if(target.className.indexOf('gem') > -1){
var value = parseInt(target.getAttribute('data-value'));
var current = parseInt( score.innerHTML );
var audio = new Audio('music/blaster.mp3');
audio.play();
score.innerHTML = current + value;
target.parentNode.removeChild(target);
if (target.className.indexOf('red') > 0){
var audio = new Audio('music/scream.mp3');
audio.play();
endGame("You lose");
}
}
return false;
}
function Remove(id) {
var gem = game.querySelector("#" + id);
if(typeof(gem) != 'undefined')
gem.parentNode.removeChild(gem);
}
function Spawn() {
var index = Math.floor( ( Math.random() * 3 ) );
var gem = gems[index];
var id = Math.floor( ( Math.random() * 1000 ) + 1 );
var ttl = Math.floor( ( Math.random() * parseInt(gem.MaxTTL) * 1000 ) + 1000 ); //between 1s and MaxTTL
var x = Math.floor( ( Math.random() * ( game.offsetWidth - 40 ) ) );
var y = Math.floor( ( Math.random() * ( game.offsetHeight - 44 ) ) );
var fragment = document.createElement('span');
fragment.id = "gem-" + id;
fragment.setAttribute('class', "gem " + gem.Class);
fragment.setAttribute('data-value', gem.Value);
game.appendChild(fragment);
fragment.style.left = x + "px";
fragment.style.top = y + "px";
setTimeout( function(){
Remove(fragment.id);
}, ttl)
}
function Stop(interval) {
clearInterval(interval);
}
function endGame( msg ) {
count = 0;
Stop(interval);
Stop(counter);
var left = document.querySelectorAll("section#game .gem");
for (var i = 0; i < left.length; i++) {
if(left[i] && left[i].parentNode) {
left[i].parentNode.removeChild(left[i]);
}
}
time.innerHTML = msg || "Game Over!";
start.style.display = "block";
UpdateScore();
}
this.Start = function() {
score.innerHTML = "0";
start.style.display = "none";
interval = setInterval(Spawn, 750);
count = 4000;
counter = null;
function timer()
{
count = count-1;
if (count <= 0)
{
endGame();
return;
} else {
time.innerHTML = count + "s left";
}
}
counter = setInterval(timer, 1000);
setTimeout( function(){
Stop(interval);
}, count * 1000)
};
addEvent(game, 'click', Click);
addEvent(start, 'click', this.Start);
HighScores();
}
JS/HTML Performance tips:
1) var gems = new Array(); change to new Array(3);
2) cache all new Audio('...');
3) use getElementById it faster then querySelector('#')
4) prepare var fragment = document.createElement('span'); and cache it, use cloneNode to insert it.
5) fragment.style.left & top change to CSS3 transform: translate(). CSS3 transform use your video card GPU, style.left use CPU...
6) Use only single setInterval and run on all list of fragments.
7) You can make a pool of "fragments" and just show\hide, it will increase performance dramatically.
Basically it works slow due to many page redraws, every time you add new elements to page browser must to redraw whole screen, do not add elements, just show\hide them.
Having a slight issue getting some coins to generate multiplayer using eureca.io websockets. I've got the players working multiplayer and from remote connections but I can't seem to get the coins to generate across the connection so they appear in the same place on all the players connections. I'm using phaser to generate the game and eureca and engine for my web connection. I've got the coins to spawn on the page with no problems, but whenever a new player joins, the coin always displays in a different place, I wondering how I can make them generate across the connection. My game code is below:
Game Code
var myId = 0;
var background;
var blueSquare;
var player;
var coins;
var goldCoin;
var squareList;
var coinList;
var cursors;
var score;
var highScore;
var ready = false;
var eurecaServer;
var eurecaClientSetup = function () {
var eurecaClient = new Eureca.Client();
eurecaClient.ready(function (proxy) {
eurecaServer = proxy;
});
eurecaClient.exports.setId = function (id)
{
myId = id;
create();
eurecaServer.handshake();
ready = true;
}
eurecaClient.exports.kill = function (id)
{
if (squareList[id]) {
squareList[id].kill();
console.log('Player has left the game ', id, squareList[id]);
}
}
eurecaClient.exports.spawnBlueSquare = function (i, x, y)
{
if (i == myId) return;
console.log('A new player has joined the game');
var blsq = new BlueSquare(i, game, blueSquare);
squareList[i] = blsq;
}
eurecaClient.exports.spawnCoins = function (c, x, y)
{
console.log('A coin has been generated');
var cn = new GenerateCoin(c, game, coin);
coinList[c] = cn;
}
eurecaClient.exports.updateState = function (id, state)
{
if (squareList[id]) {
squareList[id].cursor = state;
squareList[id].blueSquare.x = state.x;
squareList[id].blueSquare.y = state.y;
squareList[id].blueSquare.angle = state.angle;
squareList[id].update();
coinList.cursor = state;
coinList.blueSquare.x = state.x;
coinList.blueSquare.y = state.y;
coinList.update();
}
}
}
BlueSquare = function (index, game, player) {
this.cursor = {
left:false,
right:false,
up:false,
down:false,
}
this.input = {
left:false,
right:false,
up:false,
down:false,
}
var x = 0;
var y = 0;
this.game = game;
this.player = player;
this.currentSpeed = 0;
this.isBlue = true;
this.blueSquare = game.add.sprite(x, y, 'blueSquare');
this.blueSquare.anchor.set(0.5);
this.blueSquare.id = index;
game.physics.enable(this.blueSquare, Phaser.Physics.ARCADE);
this.blueSquare.body.immovable = false;
this.blueSquare.body.collideWorldBounds = true;
this.blueSquare.body.setSize(34, 34, 0, 0);
this.blueSquare.angle = 0;
game.physics.arcade.velocityFromRotation(this.blueSquare.rotation, 0, this.blueSquare.body.velocity);
}
BlueSquare.prototype.update = function () {
var inputChanged = (
this.cursor.left != this.input.left ||
this.cursor.right != this.input.right ||
this.cursor.up != this.input.up ||
this.cursor.down != this.input.down
);
if (inputChanged)
{
if (this.blueSquare.id == myId)
{
this.input.x = this.blueSquare.x;
this.input.y = this.blueSquare.y;
this.input.angle = this.blueSquare.angle;
eurecaServer.handleKeys(this.input);
}
}
if (this.cursor.left)
{
this.blueSquare.body.velocity.x = -250;
this.blueSquare.body.velocity.y = 0;
}
else if (this.cursor.right)
{
this.blueSquare.body.velocity.x = 250;
this.blueSquare.body.velocity.y = 0;
}
else if (this.cursor.down)
{
this.blueSquare.body.velocity.x = 0;
this.blueSquare.body.velocity.y = 250;
}
else if (this.cursor.up)
{
this.blueSquare.body.velocity.x = 0;
this.blueSquare.body.velocity.y = -250;
}
else
{
this.blueSquare.body.velocity.x = 0;
this.blueSquare.body.velocity.y = 0;
}
}
BlueSquare.prototype.kill = function () {
this.alive = false;
this.blueSquare.kill();
}
GenerateCoin = function (game, goldCoin) {
this.game = game;
this.goldCoin = goldCoin;
x = 0;
y = 0;
this.coins = game.add.sprite(x, y, 'coin');
game.physics.enable(this.coins, Phaser.Physics.ARCADE);
this.coins.body.immovable = true;
this.coins.body.collideWorldBounds = true;
this.coins.body.setSize(16, 16, 0, 0);
}
window.onload = function() {
function countdown( elementName, minutes, seconds )
{
var element, endTime, hours, mins, msLeft, time;
function twoDigits( n )
{
return (n <= 9 ? "0" + n : n);
}
function updateTimer()
{
msLeft = endTime - (+new Date);
if ( msLeft < 1000 ) {
element.innerHTML = "Game Over!";
} else {
time = new Date( msLeft );
hours = time.getUTCHours();
mins = time.getUTCMinutes();
element.innerHTML = (hours ? hours + ':' + twoDigits( mins ) : mins) + ':' + twoDigits( time.getUTCSeconds() );
setTimeout( updateTimer, time.getUTCMilliseconds() + 500 );
}
}
element = document.getElementById( elementName );
endTime = (+new Date) + 1000 * (60*minutes + seconds) + 500;
updateTimer();
}countdown( "countdown", 5, 0 );
}
var game = new Phaser.Game(700, 600, Phaser.AUTO, 'Square Hunt', { preload: preload, create: eurecaClientSetup, update: update, render: render });
function preload () {
game.load.spritesheet('coin', 'assets/coin.png', 18, 18);
game.load.image('blueSquare', 'assets/blue-square.png');
game.load.image('redSquare', 'assets/red-square.png');
game.load.image('earth', 'assets/sand.png');
}
function create () {
game.world.setBounds(0, 0, 1500, 1500);
game.stage.disableVisibilityChange = true;
background = game.add.tileSprite(0, 0, 800, 600, 'earth');
background.fixedToCamera = true;
squareList = {};
player = new BlueSquare(myId, game, blueSquare);
squareList[myId] = player;
blueSquare = player.blueSquare;
blueSquare.x = Math.floor(Math.random() * 650);
blueSquare.y = Math.floor(Math.random() * 650);
blueSquare.bringToTop();
game.camera.follow(blueSquare);
game.camera.deadzone = new Phaser.Rectangle(150, 150, 500, 300);
game.camera.focusOnXY(0, 0);
cursors = game.input.keyboard.createCursorKeys();
coinList = {};
goldCoin = new GenerateCoin(game, coins);
coinList = goldCoin;
coins = goldCoin.coins;
coins.x = Math.floor(Math.random() * 650);
coins.y = Math.floor(Math.random() * 650);
coins.bringToTop();
}
function update () {
if (!ready) return;
game.physics.arcade.collide(blueSquare, coins);
player.input.left = cursors.left.isDown;
player.input.right = cursors.right.isDown;
player.input.up = cursors.up.isDown;
player.input.down = cursors.down.isDown;
player.input.fire = game.input.activePointer.isDown;
player.input.tx = game.input.x+ game.camera.x;
player.input.ty = game.input.y+ game.camera.y;
background.tilePosition.x = -game.camera.x;
background.tilePosition.y = -game.camera.y;
for (var i in squareList)
{
if (!squareList[i]) continue;
var curBlue = squareList[i].blueSquare;
for (var j in squareList)
{
if (!squareList[j]) continue;
if (j!=i)
{
var targetBlue = squareList[j].blueSquare;
}
if (squareList[j].isBlue)
{
squareList[j].update();
}
}
}
}
function test(){
console.log('collsion');
}
function render () {}
Server.js Files
var express = require('express')
, app = express(app)
, server = require('http').createServer(app);
app.use(express.static(__dirname));
var clients = {};
var EurecaServer = require('eureca.io').EurecaServer;
var eurecaServer = new EurecaServer({allow:['setId', 'spawnBlueSquare', 'spawnCoins', 'kill', 'updateState']});
eurecaServer.attach(server);
eurecaServer.onConnect(function (conn) {
console.log('New Client id=%s ', conn.id, conn.remoteAddress);
var remote = eurecaServer.getClient(conn.id);
clients[conn.id] = {id:conn.id, remote:remote}
remote.setId(conn.id);
});
eurecaServer.onConnectionLost(function (conn) {
console.log('connection lost ... will try to reconnect');
});
eurecaServer.onDisconnect(function (conn) {
console.log('Client disconnected ', conn.id);
var removeId = clients[conn.id].id;
delete clients[conn.id];
for (var c in clients)
{
var remote = clients[c].remote;
remote.kill(conn.id);
}
});
eurecaServer.exports.handshake = function()
{
for (var c in clients)
{
var remote = clients[c].remote;
for (var cc in clients)
{
var x = clients[cc].laststate ? clients[cc].laststate.x: 0;
var y = clients[cc].laststate ? clients[cc].laststate.y: 0;
remote.spawnBlueSquare(clients[cc].id, x, y);
}
}
}
eurecaServer.exports.handleKeys = function (keys) {
var conn = this.connection;
var updatedClient = clients[conn.id];
for (var c in clients)
{
var remote = clients[c].remote;
remote.updateState(updatedClient.id, keys);
clients[c].laststate = keys;
}
}
server.listen(8000);
Instead of generating coins randomly positioned on each client, you could generate this random position(x and y) in server side(on conexion event) and then send this position to all clients so they can generate the coin in the same position.