Jump to content

Javascript: wait, sleep, etc. Slow-executing loop (Traffic light)


Recommended Posts

Posted (edited)

So apparently as part of the Computing course, our kids have to program a traffic light in Javascript.

 

Stage 1 is programming it to operate each stage on button-click (red, red-amber, green, amber)

Red.jpg


Start Sequence


<br />
var lightStage = [<br />
    "Red Amber.jpg",<br />
    "Green.jpg",<br />
    "Amber.jpg",<br />
    "Red.jpg",<br />
];<br />
var seqPos = 0;<br />
function changeTraffic() {<br />
	var lightImage = document.getElementById('lightImg')<br />
	lightImage.src=lightStage[seqPos];<br />
	++seqPos<br />
	if (seqPos >= lightStage.length) seqPos = 0;<br />
}<br />

Every time someone clicks 'Start Sequence', it progresses through, as it should. Once seqPos gets too high, it is reset to 0.

 

Second stage is that they have to click a button to start the sequence, which will then run automatically until they click a button that stops the sequence. This is where the Computing staff are having difficulty, so they've passed it to me and I can't figure it out, either.

 

<br />
var lightStage = [<br />
    "Red Amber.jpg",<br />
    "Green.jpg",<br />
    "Amber.jpg",<br />
    "Red.jpg",<br />
];<br />
var seqPos = 0;<br />
function changeTraffic() {<br />
	for (seqPos = 0; seqPos < 4; seqPos++) {<br />
		var lightImage = document.getElementById('lightImg')<br />
		lightImage.src=lightStage[seqPos];<br />
		setTimeout(changeTraffic, 2000);<br />
	}<br />
}<br />

Now, this works, in a fashion. It runs through the loop successfully, but since there's no pause/wait/sleep/etc, it appears to instantly run through the sequence back to red (so it doesn't look like it's doing anything, but it is, confirmed by changing red.jpg in the list to something else.)

 

Closest I've managed to get is a 'good enough' multiple functions

<br />
function changeTraffic() {<br />
	setTimeout(RedAmberLight, 2000);<br />
	setTimeout(GreenLight, 4000);<br />
	setTimeout(AmberLight, 6000);<br />
	setTimeout(RedLight, 8000);<br />
}<br />
function RedAmberLight() {<br />
	var image = document.getElementById('Light');<br />
	image.src="Red Amber.jpg";<br />
}<br />
function GreenLight() {<br />
	var image = document.getElementById('Light');<br />
	image.src="Green.jpg";<br />
}<br />
function AmberLight() {<br />
	var image = document.getElementById('Light');<br />
	image.src="Amber.jpg";<br />
}<br />
function RedLight() {<br />
	var image = document.getElementById('Light');<br />
	image.src="Red.jpg";<br />
}<br />

But surely there's got to be a better way?

Edited by Garacesh
Posted

Here's my attempt at this challenge :)

 

Tried to keep it simple and with comments where necessary.

 



Start Sequence
Stop Sequence


<br />
var TrafficLights = (function() {<br />
<br />
	// The image<br />
	var imageTag = document.getElementById("lightImg");<br />
	// Keep track of whether the sequence is running<br />
	var running = false;<br />
	// The number of seconds between light changes<br />
	var interval = 1;<br />
	// Different stages of the traffic light<br />
	var stages = [<br />
		"Red.jpg",<br />
		"Red Amber.jpg",<br />
		"Green.jpg",<br />
		"Amber.jpg"<br />
	];<br />
	// Current stage of the traffic light<br />
	var stage = 0;<br />
	// Timer for automatically changing light<br />
	var timer = null;<br />
<br />
	/**<br />
	 * Start the traffic light sequence<br />
	 *<br />
	 */<br />
	function start() {<br />
		// Mark that the light sequence is running<br />
		running = true;<br />
		// Tell the light to change<br />
		changeLight();<br />
	}<br />
<br />
	/**<br />
	 * Stop the sequence from running<br />
	 *<br />
	 */<br />
	function stop() {<br />
		// Mark that the sequence is not running<br />
		running = false;<br />
		// Stop the automatic timer from running<br />
		clearInterval(timer);<br />
	}<br />
<br />
	/**<br />
	 * Change the light to the next one in the sequence<br />
	 *<br />
	 */<br />
	function changeLight() {<br />
<br />
		// If the timer is not running, this function does not need to do anything<br />
		if (running === false) {<br />
			clearInterval(timer);<br />
			return;<br />
		}<br />
<br />
		// If the current stage gets higher than the nubmer of stages there are, reset to 0<br />
		if (stage >= stages.length) {<br />
			stage = 0;<br />
		}<br />
<br />
		// Get the image from the list of stages<br />
		var image = stages[stage];<br />
		// Update the image tag<br />
		imageTag.src = image;<br />
		imageTag.alt = image;<br />
		// Increase the current stage by 1<br />
		stage++;<br />
		// Set a timeout to change the light at the next interval<br />
		timer = setTimeout(changeLight, interval * 1000);<br />
	}<br />
<br />
	// These functions will be available on the `TrafficLights` object to allow interaction<br />
	return {<br />
		start: start,<br />
		stop: stop<br />
	}<br />
<br />
})();<br />

  • Thanks 2
Posted

Wow, you're serious? All that just because it doesn't have a sleep()? :doh:

I've passed it on to the teacher in question. Thanks a bunch.

 

I'd have thought that if the exam board want kids to be coding something, they would have provided their own examples of it being done 'properly' to mark against? :confused: Apparently not.

  • 1 year later...
Posted (edited)

Traffic Lights can't always be the same duration in every light....

 

So, i started to expand this html code..

The improved code with different seconds in every light (Credits from Webman):

 




Start Sequence
Stop Sequence
<br />
// Traffic Lights v1.1 (From Webman)<br />
// Thank you very much (Webman) for creating such impressive ideas!<br />
// Improved with different durations in every light!<br />
// But in this script, i will use input tag instead<br />
var TrafficLights = (function() {    <br />
// The image   <br />
var imageTag = document.getElementById("lightImg");   <br />
// Keep track of whether the sequence is running    <br />
var running = false;    <br />
// Different stages of the traffic light (Also defines the light)    <br />
var stages = [      <br />
  {        <br />
    "name": "green",        <br />
    "path": "green.jpg"      <br />
  },      <br />
  {        <br />
    "name": "yellow",        <br />
    "path": "yellow.jpg"      <br />
  },      <br />
  {        <br />
    "name": "red",        <br />
    "path": "red.jpg"      <br />
  }    <br />
];    <br />
// Different amount of seconds in every light change (Must be an positive integer!)<br />
var seconds_every_step = [<br />
  6,      <br />
  1,      <br />
  4    <br />
];    <br />
// Current stage of the traffic light <br />
var stage = 0;    <br />
// Current steps of the traffic light   <br />
var steps = 0;   <br />
// Timer for automatically changing light    <br />
var timer = null;   <br />
/**     * Start the traffic light sequence     *     */    <br />
function start() {        <br />
  // Mark that the light sequence is running        <br />
  running = true;        <br />
  // Tell the light to change        <br />
  changeLight();    <br />
}    <br />
<br />
/**     * Stop the sequence from running     *     */   <br />
 function stop() {<br />
  // Mark that the sequence is not running        <br />
  running = false;        <br />
  // Stop the automatic timer from running        <br />
  clearInterval(timer);    <br />
}    <br />
<br />
/**     * Change the light to the next one in the sequence     *     */<br />
function changeLight() {        <br />
  // If the timer is not running, this function does not need to do anything        <br />
  if (running === false) {            <br />
    clearInterval(timer);            <br />
    return;        <br />
  }        <br />
  else {        <br />
  };<br />
<br />
  // If the current stage gets higher than the number of stages there are, reset to 0        <br />
  if (stage >= stages.length) {            <br />
    stage = 0;        <br />
  }        <br />
  else { <br />
  };           <br />
  // If the current steps gets higher than the number of seconds in a step there are, reset to 0    <br />
  if (steps >= seconds_every_step.length) {<br />
    steps = 0;<br />
  }        <br />
  else {<br />
  };        <br />
<br />
  // Get the image from the list of stages        <br />
  var image = stages[stage];        <br />
  var wait_seconds = seconds_every_step[steps];<br />
  // Update the image tag and defines the light name<br />
  imageTag.src = image.path;<br />
  imageTag.alt = String("Traffic light color is " + image.name + ".");       <br />
<br />
  // Increase the current stage by 1        <br />
  stage++;        <br />
  // Increase the current steps by 1        <br />
  steps++;        <br />
  // Set a timeout to change the light at the next interval        <br />
  timer = setTimeout(changeLight, wait_seconds * 1000);    <br />
}    <br />
// These functions will be available on the `TrafficLights` object to allow interaction    <br />
return {        <br />
  start: start,        <br />
  stop: stop    <br />
}<br />
})();<br />

Notes!

If the variable "seconds_every_step" is more than 3 lengths like this:

var seconds_every_step = [

20,

40,

32,

5

]

 

It means that the every light based on the variable "stage" will return like this:

Green light: 20 seconds,

Yellow light: 40 seconds,

Red light: 32 seconds,

Green light: 5 seconds,

Yellow light: 20 seconds (repeated step),

Red light: 40 seconds,

...

Edited by Zamy_Arkre_Nendmed
Want to add more statements...
Posted

Sorry, just stumbled across this before turning in and thought I'd chip in. Webman's solution is a good one with a few nice features but your first attempt was actually pretty close to a minimalist solution. Just do away with the for loop in the function that changes the image and it should behave more as you expect (i.e. only advance the image one stage each time the function is called). Here's an example of something similar that you can play with:





Cycle
/div>
<br />
	var stages = [<br />
		'red','orange','green'<br />
	];<br />
	var current_stage = 0;<br />
	var panel = document.getElementById('panel');<br />
	function cycle(){<br />
		if(current_stage >= stages.length) current_stage = 0;<br />
		panel.style.background = stages[current_stage];<br />
		current_stage++;<br />
		setTimeout(cycle, 1000);<br />
	}<br />



  • Thanks 1
Posted (edited)

Here's my attempt (expanding @spadam):





Cycle



<br />
  var stage = 0;<br />
  var red = document.getElementById('red');<br />
  var amber = document.getElementById('amber');<br />
  var green = document.getElementById('green');<br />
  function cycle() {<br />
      switch(stage) {<br />
        case 0:<br />
          red.style.background = 'red';<br />
          amber.style.background = 'black';<br />
          break;<br />
        case 1:<br />
          amber.style.background = 'orange'; <br />
          break;<br />
        case 2:<br />
          green.style.background = 'green';<br />
          red.style.background = 'black';<br />
          amber.style.background = 'black';<br />
          break;<br />
        case 3:<br />
          green.style.background = 'green';<br />
          break;<br />
        case 4:<br />
          green.style.background = 'black';<br />
          amber.style.background = 'orange';<br />
      }<br />
      (stage==4) ? stage=0 : stage++; // a fancy way to do - if(stage==4){stage=0} else{stage++;}<br />
      setTimeout(cycle, 1000);<br />
  }<br />



Edited by ReadTheNetwork
Posted
Nice addition.. Stick
border-radius: 25px;

on those divs and it's almost the real thing!

 

Better? :) - even added some flair

 



<br />
#back {<br />
  width: 75px;<br />
  height: 180px;<br />
  background: black;<br />
  text-align: center;<br />
}<br />
div.light {<br />
transition: background-color 0.5s ease;<br />
  margin: auto;<br />
  width:50px; <br />
  height:50px; <br />
  background: grey;<br />
  border-radius: 25px;<br />
  padding: 5px;<br />
}<br />



Cycle









<br />
  var stage = 0;<br />
  var red = document.getElementById('red');<br />
  var amber = document.getElementById('amber');<br />
  var green = document.getElementById('green');<br />
  function cycle() {<br />
      switch(stage) {<br />
        case 0:<br />
          red.style.background = 'red';<br />
          amber.style.background = 'grey';<br />
          break;<br />
        case 1:<br />
          amber.style.background = 'orange'; <br />
          break;<br />
        case 2:<br />
          green.style.background = 'green';<br />
          red.style.background = 'grey';<br />
          amber.style.background = 'grey';<br />
          break;<br />
        case 3:<br />
          green.style.background = 'green';<br />
          break;<br />
        case 4:<br />
          green.style.background = 'grey';<br />
          amber.style.background = 'orange';<br />
      }<br />
      (stage==4) ? stage=0 : stage++; // a fancy way to do - if(stage==4){stage=0} else{stage++;}<br />
      setTimeout(cycle, 1500);<br />
  }<br />



  • Thanks 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now



×
×
  • Create New...