Wrestling Javascript with Love

For some time I has ben tryiing to write a game in javascript on the web. Nothing fancy, something that could be written in one hour of work, and perhaps be expansible.

I have failed in epic proportions, I probably suck at life because of that, but… oh well. At least pets love me. I am not totally a bad man.

I am going to document here the history of a failure.

Episode 1

So I was tryiing to make this game for some time, and I decided that would be a strategic game, with dwarfs in it, a strategy game where you control a expedition traveling down the ground, visiting lost civilizations, hiring weird races. A strategy game, part Dwarf Fortres, part Roguelike part Webgame.

Since I am a app programmer, my first attemp was overengineer. I am too lazy to publish all the files involved, so I can’t show a whole image of the failure.

Heres the html file


<!DOCTYPE html>
<html>
<head>
<title>Expedition1</title>

    <link rel="stylesheet" href="css/main.css"> 

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="ayudas.js"></script>
<script src="gui.js"></script>
<script src="razas.js"></script>
<script src="mundo.js"></script>
    
    
    <!-- pantallas -->
<script src="reclutar.js"></script>
    
    
    <!-- arrancar -->
<script src="arranquefrio.js"></script>

<meta charset=utf-8 />


</head>
<body>
  <div id="lore" class="bloque">
  <p id="lore_arranca"></p>
  </div>
  
  
  <div id="hire" class="bloque">
  <table>
  <thead>
  <!-- <tr><td>Race</td><td>Name</td><td>Price</td><td>[hire button]</td></tr> --> 
  </thead>
  <tbody id="listaSeresContratar">
 
      <!-- cosas aqui -->
  <tbody>
     </table>  
     <input type=button id=reroll value=Reroll><br>
  Party size: <input type=text id=partysize>
      </div>
      
      
  <div id="fase1"  class="bloque">      
  
  </div>
      
      
      
</body>
</html>

Heres one of the library files




var mundo = (function() {
    var fase_interface =  "arranca";
    
    function CargarLore(texto){//esta funcion es una mierda, es demasiado especializada
        $.get('textos/'+texto+'.htm', function(data) {
            $("#lore_"+texto).html(data);
            var avanzaboton = gui.botonContinua("contratarMiembros",function(){ return true;});
            $("#lore_"+texto).append(avanzaboton);
        });
    }
    
    function empieza_contratarMiembros(){
        $("#lore").hide();
        $("#hire").show();
     
        reclutar.generarPantalla();     
    }
    
    
    
    return {
     arranca:function(){
        //ocultamos todos los elementos de interface, se mostraran cuando sea lo adecuado    
        $(".bloque").hide();                  
        CargarLore("arranca");//cargamos este texto en lore
        //CargarLore("test");//cargamos este texto en lore
        $("#lore").show();
     },
     empiezaFase:function(fase){
         switch(fase){
             case "contratarMiembros":
                 empieza_contratarMiembros();
                 break;
            case "fase1":
                empieza_fase1();
                break;                 
            default:
                alert("ERROR: fase "+fase+" no implementada");
                break;
         }         
     }
    };
})();

This one failed, and I don’t learned much from it. Other that code can get quickly complex if it start complex from that start.

Episode 2

Sudently, I decided the game would be even cooler in a Victorian Era world, with a rich underground world. With all the nice characters from the victorian era and nearby.

I made the code more compact, the right thing…




var juego = (function(){       
    return {
	rolemaster:function(){}
    };
    
})();


juego.toggleBoton = (function(juego){       
    return {
        start:function(boton,texto){	
		$(boton).val(texto);				
		juego.toggleBoton.set(boton,0);//empieza apagado
	},
	set:function(boton,encendido){
		$(boton).attr("data-onoff",encendido);
		if(encendido==1) {		
			$(boton).removeClass("botonApagado")
			.addClass("botonEncendido");
		} else {
			$(boton).removeClass("botonEncendido")
			.addClass("botonApagado");		
		}						
	},	
	click:function(boton){
		//console.log("boton:click!");
		var estado = $(boton).attr("data-onoff");
		var nuevoEstado = (estado==1)?0:1;						
		//console.log("old:"+estado+",nuevo:"+nuevoEstado);
		juego.toggleBoton.set(boton,nuevoEstado);
	},	
	boton:function(boton,text){
		juego.toggleBoton.start(boton,text);
		$(boton).click(function(){
			juego.toggleBoton.click(boton);
		});		
	}	
    };
    
})(juego);


juego.rolemaster = (function(juego){       
    return {
        contratar:function(boton){	
		var nombre = $(boton).attr("data-nombre");
		console.log("Contratando:"+nombre);	
	},
	turno:function(){
		alert("se juega turno");		
	},
    };
    
})(juego);


$(function(){

	console.log("Creando enlaces");
	/* Enlaces, eventos  */
	$(".siguientefase").click(function(){
		console.log("boton:siguientefase");		
		juego.rolemaster.turno();
	});
	

	$("input.contratar").each(function(){
		juego.toggleBoton.boton(this,"Hire");		
	});
	
	
});


/*
- Captain Nemo - Inventive - Freedom defender - Outlaw
- Sherlock Holmes - Deductive - Resourcefull - Drug Addict
- Otto Lidenbrock - Academic - Lucky - Curious 
- Mark Twain - Pragmatical - Industrious - Unlucky
- Doctor Henry Jekyll - Inventive - Pragmatical - Bipolar
- Alice - Curious - Lucky - Logical
- Geppetto  - Puppetmaster - Woodcutter - Unlucky
- Dorian Gray - Inmortal - Cursed - Evil 
- Thomas Edison - Industrious - Evil - Lucky 
- Nikola Tesla - Technician - Unlucky - Experimental
- Caliban - Unlucky - Inspired - Beastly 
*/

Too bad, I failed for the classical trap of newbie game programmers: never start a game writting the menu. If the first thing you write is the menu, is said that will probably be the last and only thing.
Here was true.
The code that was created was no too bad, but did nothing, other than ugly interfaces. The code was doing stuff, and not being very complex, and have some room to wrong. So is not too bad a attemp.

…[CONTINUE]…

Episode 3

Learning from this experiences, I tried again. This time doing something much more compact. Totally focused on solving the problem at hand, not building architecture.

I created a RTS engine, a complete one, … I mismanaged part of the instantation part, so is mostly a single civilization building stuff that dies in fake battles, but has all the most important parts of a rts engine. I declare that a success.



var rand = function(){
	return Math.random()*100;
};

var random = (function(){   
	return {
		betwen:function(min,max){
			return Math.random()*(max-min)+min;
		},
		element:function(datarray){	
			var len = datarray.length;
			if(!len) return;
			var r = parseInt(this.betwen(0,len),10);
			return datarray[r];
		}	
	};
})();

var scroll = (function(){        
    var index = 0;
    var maximo = 10;
    
    return {
	cortarViejas:function(){
		var edadCorte = index - maximo;
	
		$(".logline").each(function(){
			if($(this).data("nace")<edadCorte)
				$(this).remove();			
		});	
	},
	add:function(txt){
		var line = $("<div>");		
		line.addClass("logline");
		line.data("nace",index++);
		line.html(txt);
				
		$(line).insertAfter('#marca');
		
		this.cortarViejas();
	},
	error:function(txt){
		this.add("ERROR: "+txt);
	},
	battle:function(txt){
		this.add("Battle: "+ txt);
	},
	oceaniaDice:function(txt){
		this.add("[Oceania broadcast] "+ txt);
	},
	debugOceania:function(txt){
		this.add("[Oceania debug] "+ txt);
	}
    };
    
})();


var oceania = (function(){        

    var tanks = 0;
    var soldiers = 0;
    var build = "";//build strategy 
    var dead = 0;
    
    var prices = {"tank":5,"soldier":1};

    var hateLines = [ "Eastasia will be destroyed","We hate Eurasia","Our troops are doing good",
	    "We have always ben at war with Eastasia","Under the spreading chestnut tree.I sold you and you sold me"];

    var metal = 0;
    var powerplants = 1;
    var factory = 3;    

    return {
	info:function(){
		scroll.debugOceania("Build strategy:"+ build);		
		scroll.debugOceania("Metal:"+ metal);		
		scroll.debugOceania("Army: soldier "+soldiers + ", tanks "+tanks );		
		scroll.debugOceania("Dead:"+ dead);		
	},
	militarTick:function(stuff){
		
		if(tanks>10 && soldiers > 20){
			tanks -= 10;
			soldiers -= 20;
			scroll.battle("Some units losts! 10 tanks, 20 soldiers");		
			dead += 10+ 20;
		}			
	},
	buy:function(stuff){	
		if(!prices[stuff]) {
			scroll.error("buying what?:"+stuff);
			return;
		}
		if(prices[stuff]>metal) {
			scroll.debugOceania(stuff + " is too expensive");
			return;
		}
		metal -= prices[stuff];					
		
		switch(stuff){
			case "tank":							
				tanks++;	
				//scroll.debugOceania("Oceania create tank"); 				
				break;
			case "soldier":			
				soldiers++;	
				//scroll.debugOceania("Oceania create soldier"); 				
				break;		
		}		
	},
	buildTick:function(){
		switch(build){
			case "tanks":
				this.buy("tank");
				break;
			case "soldiers":
				this.buy("soldier");
				break;
		}
	},
	strategyTick:function(){		
		var likeSoldiers=0,likeTanks=0;
		
		build = "nothing";
		
		//You have something, build something!
		if( metal> 10) //we have metal, lets buy soldiers!
			likeSoldier = 10;	
			
		//minimum this	
		if ( tanks < 10)
			likeTanks += 10;
		if ( soldiers< 20)
			likeSoldiers += 10;
		
		//we want 5 soldiers for 1 tank
		if ( soldiers < tanks * 5)
			likeSoldiers += 10;
	
		//if we have at least 40 soldiers, we would love to have more tanks!
		if ( soldiers > 40) 
			likeTanks += 10;
			
		//if we have at least 40 tanks, we would love to have moresoldiers!
		if ( tanks > 40) 
			likeSoldiers += 10;
		
		//we want soldiers more than anything else, lets build soldiers!
		if(likeSoldiers > likeTanks ){		
			build = "soldiers";
		}else 
		//we want tanks more than anything else, lets build soldiers!
		if(  likeTanks > 5) {
			build = "tanks";
		}				
	},
	produccionTick:function(){
		var t;
		var energy = powerplants * 3;
		var factorycount = factory;
		var metalInitial = metal;
		
		for(t=0;t<energy,factorycount>0;t++,factorycount--){			
			metal = metal + 1;		
		}			
		//var produce = metal - metalInitial;
		//scroll.debugOceania("Oceania create "+ produce + " metal. Total now:"+metal); 		
	},
	propagandaTick:function(){
		if(rand()<5) {
			var hate = random.element(hateLines);
			scroll.oceaniaDice(hate);
		}	
	},
	tick:function(){
		this.propagandaTick();
		this.produccionTick();
		this.strategyTick();
		this.buildTick();
		this.militarTick();
	}	
    };
    
})();



$(function(){
	$("#cmd").keyup(function(event){		    		
		if (event.which == 13) {
			var command = $(this).val();
			
			$(this).val("");
			scroll.add(command);
			
			$("#cmd").focus();
			
			event.preventDefault();
		}  
	});
	
	
	$("#reload").click(function(){	
		document.location = "game.htm?r="+Math.random();
	});
	
	oceania.tick();
	
	$(document).ready(function(){
		setInterval(function(){
			oceania.tick();
		},300);
	});		
});

Number of players: Zero.
That could be a problem.

Episode 4

I am going back to the original idea, and building something that is even more compact, and more solution focused. The hell with building reusable libraries. I am tryiing to follow the idea to build a prototype first. Failing that that, but now I know what I have to do.


var rand = {};
var random = {};
var consola = {};
var mundo = {};


 $(function(){   
	rand = function(){
		return Math.random()*100;
	};

	random.entre = function(min,max){
		return parseInt(Math.random()*(max-min)+min,10);
	};
	
	random.decien = function(cuantos){	
		return random.entre(0,100)<cuantos;
	}
	
	random.elemento = function(datarray){	
		var len = datarray.length;
		if(!len) return;
		var r = parseInt(this.entre(0,len),10);
		return datarray[r];
	
	};
});


$(function(){        
    var index = 0;
    var maximo = 10;
    
	consola.cortarViejas = function(){
		var edadCorte = index - maximo;
	
		$(".logline").each(function(){
			if($(this).data("nace")<edadCorte)
				$(this).remove();			
		});	
	};
	
	consola.add = function(txt){
		var line = $("<div>");		
		line.addClass("logline");
		line.data("nace",index++);
		line.html(txt);
				
		$(line).insertAfter('#marca');
		
		consola.cortarViejas();
	};
	
	consola.error = function(txt){
		consola.add("ERROR: "+txt);
	};
	
	consola.battle  =  function(txt){
		consola.add("Battle: "+ txt);
	};
	
	consola.dwarf  =  function(txt){
		consola.add("Your dwarvens say: <i>"+ txt+"</i>");
	};	
	consola.game = function(txt){
		consola.add("[game] "+ txt);
	};
	consola.debug = function(txt){
		consola.add("[debug] "+ txt);
	};    
});


$(function(){

	var nivelactual = 1;
	var food = 50;
	
	var $yes = $("#yes");
	var $no = $("#no");

	mundo.iniciaValores = function(){
		nivelactual = 1;
		food = 50;		
	};

	mundo.expedicionNace = function(){
		consola.game("A new dwarf expedition start.")
		mundo.iniciaValores();				
	};


	mundo.expedicionMuere = function(){
		consola.game("Your expedition dies.")
		consola.game("The game will start again.")
		mundo.iniciaValores();		
	};

	mundo.evento = function(clave){
		switch(clave){
			case "food/0":
			mundo.expedicionMuere();
			break;
			default:
			consola.debug(clave);
			break;
		}
	};

	mundo.muerteInanicion = function(){	
		if(food<=0){
			food = 0;
			throw "food/0";		
		}		
	};
	
	mundo.bindings_borra = function(){	
		mundo.bindings_reset(false);		
	
		$yes.click(function(){		
			consola.game("You says 'Yes!', for no reason whatsoever.");
		});
		$no.click(function(){
			consola.game("You says 'Noo' very loud to the darkness.");
		});	
		
	};
	
	mundo.bindings_reset = function(activos){
		$yes.unbind('click');
		$no.unbind('click');

		if(activos){
			$yes.removeAttr("disabled");
			$no.removeAttr("disabled");				
		} else {
			$yes.attr("disabled", "disabled");
			$no.attr("disabled", "disabled");		
		}		
	}
	
	
	mundo.bindings_altarHongo = function(){
		mundo.bindings_reset(true);
	
		$yes.click(function(){		
			var extrafood = random.entre(0,20);
			food += extrafood;
			consola.game("The Fungus God give you "+extrafood+" funcows as food. You have now food for "+food+" days.");
		
			mundo.bindings_borra();			
		});			

		$no.click(function(){
			consola.game("You nod, has like praying to the Fungus God would be a bad idea.");
			mundo.bindings_borra();
		});	
		
	};
	
	
	mundo.temploHongo = function(){	
		if( random.decien(10) ){
			temploHongo = true;			
			consola.dwarf("Sire!, we have found a Fungus altar. Do we have to pray in it?");			
			mundo.bindings_altarHongo();
		}
	};

	mundo.bajarnivel = function(){
		mundo.bindings_borra();	
		
		nivelactual += 1;
		food -= 1;		
		consola.game("The expedition has descenced one level. " + "The deep is now "+nivelactual+". " 
		+	"We have food for  "+food+" days." );	

		try {			
			mundo.muerteInanicion();
			mundo.temploHongo();
		}catch(e){
			mundo.evento(e);			
		}
		
	};

});


$(function(){
	$("#cmd").keyup(function(event){		    		
		if (event.which == 13) {
			var command = $(this).val();
			
			$(this).val("");
			consola.add(command);
			
			$("#cmd").focus();
			
			event.preventDefault();
		}  
	});
	
	
	$("#reload").click(function(){	
		document.location = "game.htm?r="+Math.random();
	});
	
	$("#bajar").click(function(){
		mundo.bajarnivel();
	});
	
	mundo.bindings_borra();
	mundo.expedicionNace();
	
});

Number of players: One.

This is actually a game. No something somebody would call a game, but is technically a game. It does one thing, and is compact enough and simple enough is not dying over his own weight like all other attemps. This one could be a start!. Yay.

Well, if that’s what failure is like, I’d say, keep going! :)

Is this the Drunk Thread: Teiman Edition?

I have rescue a old greasemonkey script that did years ago for a forum. Is poorly ported, and translated to english. Is not as fun with generic gravatars (the original used a ranks from the forum and a lot of other information… here is generated randomly) and names like player14, but heres anyway. A random battle log creator :D

http://jsbin.com/egojix/

player11 upgrade his weapon to level 1
player11 shot with a pistol to player23 with 64 of damage
4 mental health lost by player90
player6 upgrade his weapon to level 1
player6 shot with a pistol to player21 with 41 of damage
player21 upgrade his weapon to level 1
player21 shot with a pistol to player1 with 35 of damage
player19 upgrade his weapon to level 1
player19 shot with a pistol to player1 with 89 of damage
player1 explode, his parts are everywhere
player90 upgrade his weapon to level 1
player90 shot with a pistol to player13 with 98 of damage
player13 is brutally murdered
player93 upgrade his weapon to level 1
player93 shot with a pistol to player14 with 95 of damage
player14 explodes and dies
player2 upgrade his weapon to level 1
player2 shot with a pistol to player6 with 43 of damage
player23 upgrade his weapon to level 1
player23 shot with a pistol to player6 with 40 of damage
player21 upgrade his weapon to level 2
player21 fire with a rifle to player94 with 41 of damage
4 mental health lost by player23
player92 upgrade his weapon to level 1
player92 shot with a pistol to player3 with 17 of damage
player44 upgrade his weapon to level 1
player44 shot with a pistol to player6 with 71 of damage
player6 is desintegrated by the explosion
player23 upgrade his weapon to level 2
player23 fire with a rifle to player7 with 47 of damage
player93 upgrade his weapon to level 2
player93 FIRE WITH A RIFLE to player2 with 110 of damage
player2 explodes and dies
player7 upgrade his weapon to level 1
player7 shot with a pistol to player3 with 18 of damage
player23 upgrade his weapon to level 1
player23 shot with a pistol to player11 with 69 of damage
player94 upgrade his weapon to level 1
player94 shot with a pistol to player3 with 64 of damage
player3 head explode
player9 upgrade his weapon to level 1
player9 shot with a pistol to player4 with 29 of damage
player8 upgrade his weapon to level 1
player8 shot with a pistol to player4 with 60 of damage
player4 head explode
player7 upgrade his weapon to level 2
player7 fire with a rifle to player5 with 21 of damage
3 mental health lost by player44
player9 upgrade his weapon to level 2
player9 fire with a rifle to player5 with 34 of damage
player44 upgrade his weapon to level 2
player44 fire with a rifle to player90 with 83 of damage
player7 upgrade his weapon to level 3
player7 launch a grenade to player23 with 24 of damage
player11 upgrade his weapon to level 2
player11 fire with a rifle to player44 with 75 of damage
player90 upgrade his weapon to level 2
player90 FIRE WITH A RIFLE to player5 with 114 of damage
player5 is desintegrated by the explosion
player12 upgrade his weapon to level 1
player12 shot with a pistol to player8 with 75 of damage
player11 upgrade his weapon to level 3
player11 launch a grenade to player21 with 86 of damage
player21 explode and his head ends 59 miles aways
player7 upgrade his weapon to level 4
player7 launch a missile to player12 with 27 of damage
player12 upgrade his weapon to level 2
player12 fire with a rifle to player8 with 87 of damage
player8 is desintegrated by the explosion
player90 upgrade his weapon to level 3
player90 LAUNCH A GRENADE to player18 with 131 of damage
player18 is desintegrated by the explosion
3 mental health lost by player92
player12 upgrade his weapon to level 3
player12 launch a grenade to player7 with 100 of damage
player7 is desintegrated by the explosion
player12 steal the weapon of level 4 of player7
player19 upgrade his weapon to level 2
player19 FIRE WITH A RIFLE to player9 with 104 of damage
player9 head explode
player23 upgrade his weapon to level 2
player23 fire with a rifle to player11 with 80 of damage
player11 is desintegrated by the explosion
player23 steal the weapon of level 3 of player11
player12 upgrade his weapon to level 5
player12 FIRE A HOWITZER to player92 with 125 of damage
player92 explode and his head ends 58 miles aways
player23 upgrade his weapon to level 3
player23 launch a grenade to player94 with 54 of damage
player94 is brutally murdered
player19 upgrade his weapon to level 3
player19 LAUNCH A GRENADE to player90 with 119 of damage
player90 is absolutelly desintegrated by the explosion
player93 upgrade his weapon to level 3
player93 LAUNCH A GRENADE to player23 with 126 of damage
player23 is absolutelly desintegrated by the explosion
player19 upgrade his weapon to level 4
player19 LAUNCH A MISSILE to player93 with 134 of damage
player93 is desintegrated by the explosion
Win the attackers!

It’s remarkable to me, every time I use it, how compact and versatile jQuery can be.

Yep, and not just that. Perhaps in the future it will important to write event based code, to code for CPU’s with more than 20 cores, so the style of JQuery programming will be natural there.

JavaScript is a terrible language and historically the language inherently assumes a non-threaded model for everything. It is easy to set things up asynchronous using timers and closures and such, but nothing is reentrant and you can’t lock resources in any way. Yeah, they are bolting on isolates in the form of ‘web workers’ to deal with this, but that API is just more cruft on top of an already shitty language.

If you want real concurrency, Go is a fantastic language. Of course, you can’t run it in a web browser, because only shitty technology like JavaScript, CSS, etc are allowed inside web browsers. It’s some kind of a law or something.

I used to opinate like that. But I have learned that is actually a very clever language and that is posible to write good code if you avoid the bad parts. Javascript is useable on the server side, with things like Node.js
The brownser is made less inconsistent with libraries like jquery that provide a single api that work everywhere.

Javascript is really a crossroad. The only modern prototipe based OOP, the c sintax, lamdas, closures, functional programming, event based programming. Only recently the power of this language has ben made visible. Theres still a lot of work to do, but it seriusly not a languaje to be dismished.

You say that like it’s a bad thing. Pure async is totally the right way to go – locking is evil. .NET is making big bets on async programming, and rightly so.

(And what do you mean “nothing is reentrant”?)

I have tried to read that article. But Is too complex to me, and the syntax of C# is obscure to me. Learning C# is on my todo list, but I never have a good excuse to put time on it.

For me writing async code is very easy. You write callbacks, and pass these callbacks to function that do I/O or expensive operations, so the flow of the program exit instantly from these operations. What its more to know?, then these callbacks support a huge part of your code, is your code, and may generate more async operations. If you have two expensive I/O operations, and you start both, your code is inmediatelly free to do something else, like calculations. With a sync style, your code need to finish the first I/O operation to start the second one, clearly wasting time, and need to finish the second I/O operation to start the calculations, wasting time again…

I have heard these words before about API calls in a OS. API calls that can break if two programs call the same API while it is still processing stuff (perhaps this OS internally use global variables?).

I don’t know what he mean. If you have a button that create a async call that last 13 seconds, you can click click click, causing a lot of requests, and all these will ignore the others. So is more like “everything is reentrant” where you perhaps would want stuff to block re-requests. But that can be solved by having a “proxy” object, so you ask stuff to the proxy, and he call the server if the data requested is not cached, and have a flag for pages requested, so is not dumb to re-request stuff. This is a easy “design pattern” that would solve the problem, with fantastic awesome side effects.

I did something like that for a “Infinite scrool” page. Where the page requested is based on the position of a scroll bar, but you never knows what page will request the client next, or what page is actually pointing the scrollbar. So theres this proxy, that ask for the current aimed page, and try to render the current aiming page. Sometimes it try to render a page that It don’t have, and request it, but by the time the page comes, the scroll is on a different position, but this is not a problem at all, since a new page is requested. And everything is smooth, and the user can scroll over 1.800.000 million elements like if the browser would support all that html :D

This is a great thread. Keep it up.

My point wasn’t that things shouldn’t be async, quite the opposite. My point was that while JavaScript is suitably async-capable, you still get boned by the fact that you’re stuck in a language that is historically inherently single-threaded. So yeah, you can not block while waiting for some network data to arrive, which is great, but those other 7 cores on your 8 core system? Wasted. Again, “web workers” seek to fix that but because of the committee-politics theater surrounding extensions to the language the API is pretty limited and still not supported in all browsers.

C# Async is great. Go goroutines with CSP is great.

JavaScript, OTOH, is a shitty language that is only widely used due to a historical accident, but unfortunately along with PHP and Ruby it has created a generation of younger programmers that are too stupid to realize how badly their languages suck.

This only works because all of your code is running in a single thread. That doesn’t make it reentrant, it just means the language is inherently incapable of doing reentry in the first place, which is why the only solution for threading in JavaScript is so limited and has forced “share nothing” state handling.

FWIW, I don’t meant to attack Teiman for using JavaScript. As Donald Rumsfeld said, you use the client-side web language you’ve got, not the one you wished you had. I’m merely pointing out that while the async facilities in JavaScript help compared to doing everything in a blocking manner, they don’t help with the “20 core” future. And the language sucks in all sorts of ways, like the shitty scoping that can’t decide if it wants to be classically OO or prototypically OO, etc.

Ah, I see: when you said “nothing is reentrant,” you meant “the language doesn’t support a threading model, and therefore the runtime is not at all thread-safe (aka reentrant).” Yes, I agree with that.

Also agreed that JavaScript isn’t at all multicore-friendly in its current state. ALSO agreed that there are all sorts of warts on JavaScript that make it rather bizarre to program in directly. (The difference between == and ===, or the weird array/map behavior of every damned object in the language, or the prototype chain… yeah, it’s definitely got some serious hair.)

Plus I just can’t live without lots of static typing – programs over 100,000 lines long (our bread and butter at work) just aren’t nearly as much fun without the compiler catching more than half of your bugs before you run anything.

The next couple of years are going to be very interesting in the production async programming language world. Trust me on this.

You’re being ridiculous. Yes, Javascript is a flawed language, and could be better, but comparing it to PHP is laughable. PHP is a fucked-up shithole of a language that poisons minds and destroys souls. Javascript is a mostly pretty nice functional language with some legacy quirks related to it having started with a rushed implementation in the mid-90s.

All else equal, I’d rather program in C# than JS (though even there, I’d probably take JS over pre-Linq C#); but given my druthers, I’d rather program in JS than in Java, C++, or even Python, in any situation that JS is used for.

I think I see the problem :)

A practical way to judge a language is for what able people to build, how expressive is and how easy is for others to read intend.

Update
A language is good:

  • allow people to write the stuff that people need
  • its easy to write
  • its easy to understand what the original programer was trying, by reading the code