Function Requirements: Simulate traffic light signals and switch green (pass), yellow (wait) and red (stop) in 5 seconds, 1.5 seconds and 3.5 seconds respectively. That is, the default is green, 5 seconds later it is yellow, 1.5 seconds later it is red, 3.5 seconds later it is green again, and so on.

# 1: create a nested state for each state

html

<ul id="traffic" class="wait">
  <li></li>
  <li></li>
  <li></li>
  <li></li>
  <li></li>
</ul>
Copy the code

css

#traffic {
  display: flex;
  flex-direction: column;
}

#traffic li{
  list-style: none;
  width: 50px;
  height: 50px;
  background-color: gray;
  margin: 5px;
  border-radius: 50%;
}

#traffic.s1 li:nth-child(1) {
  background-color: #a00;
}

#traffic.s2 li:nth-child(2) {
  background-color: #aa0;
}

#traffic.s3 li:nth-child(3) {
  background-color: #0a0;
}

#traffic.s4 li:nth-child(4) {
  background-color: #a0a;
}

#traffic.s5 li:nth-child(5) {
  background-color: #0aa;
}
Copy the code

js

const traffic = document.getElementById('traffic'); (function reset(){// By default, the traffic className property is set to 's1' traffic.className = 's1'; setTimeout(function(){ traffic.className = 's2'; setTimeout(function(){ traffic.className = 's3'; setTimeout(function(){ traffic.className = 's4'; setTimeout(function(){ traffic.className = 's5'; setTimeout(reset, 1000) }, 1000) }, 1000) }, 1000) }, 1000); }) ();Copy the code

We start by taking the class=traffic element and declaring a reset function. This function sets the traffic element’s className property to ‘s1’ by default. When this is done, it matches the CSS setting below, making the background of the Li element green. Then there are several nested setTimeOut methods.

Disadvantages:

1. If we change the HTML code, the element will not be called traffic and the function will not work. 2. If we want to reuse this function elsewhere, we have to recreate the traffic object there. # Method 2: Abstract data to do state switch

html

<ul id="traffic" class="wait">
  <li></li>
  <li></li>
  <li></li>
</ul>
Copy the code

css

#traffic { display: flex; flex-direction: column; } #traffic li { display: inline-block; width: 50px; height: 50px; background-color: gray; margin: 5px; border-radius: 50%; } #traffic.stop li:nth-child(1) { background-color: #a00; } #traffic.wait li:nth-child(2) { background-color: #aa0; Pass li:nth-child(3) {background-color: #0a0;} // Set the first li element under the class=traffic =pass element to green #traffic. }Copy the code

JS

Const traffic = document.getelementById ('traffic'); const traffic = document.getelementById ('traffic'); Const stateList = [{state: 'wait', last: 1000}, {state: 'stop', last: 3000}, {state: 'pass', last: 3000},]; Function start(traffic, stateList){function applyState(stateIdx) {const {state, last} = stateList[stateIdx]; traffic.className = state; setTimeout(() => { applyState((stateIdx + 1) % stateList.length); }, last) } applyState(0); } start(traffic, stateList);Copy the code

Data abstraction is the definition and aggregation of data into objects that can be processed by a particular process. Simply put, it is the structuring of data. Data abstracted code can be adapted to business needs at different states and times, and we only need to modify the data abstraction without changing the start method.

Disadvantages:

After data abstraction refactoring, our start method is not yet fully encapsulated. Because the start function has a part of the code that changes the external state. We call the part of the code that changes the external state side effects. In general, we can consider stripping out some of the code that has side effects in the function body, which often improves the versatility, stability, and testability of the function.

Method 3: The process abstracts out a polling method

html

<ul id="traffic" class="wait">
  <li></li>
  <li></li>
  <li></li>
</ul>
Copy the code

css

#traffic {
  display: flex;
  flex-direction: column;
}

#traffic li{
  display: inline-block;
  width: 50px;
  height: 50px;
  background-color: gray;
  margin: 5px;
  border-radius: 50%;
}

#traffic.stop li:nth-child(1) {
  background-color: #a00;
}

#traffic.wait li:nth-child(2) {
  background-color: #aa0;
}

#traffic.pass li:nth-child(3) {
  background-color: #0a0;
}
Copy the code

js

const traffic = document.getElementById('traffic'); function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function poll(... fnList){ let stateIndex = 0; return async function(... args){ let fn = fnList[stateIndex++ % fnList.length]; return await fn.apply(this, args); } } async function setState(state, ms){ traffic.className = state; await wait(ms); // let trafficStatePoll = poll(setstate.bind (null, 'wait', 1000), setstate.bind (null, 'stop', 3000), setState.bind(null, 'pass', 3000)); (async function() { // noprotect while(1) { await trafficStatePoll(); }} ());Copy the code

# Method 4: Wrap the Primise object

html

<ul id="traffic" class="wait">
  <li></li>
  <li></li>
  <li></li>
</ul>
Copy the code

css

#traffic {
  display: flex;
  flex-direction: column;
}

#traffic li{
  display: inline-block;
  width: 50px;
  height: 50px;
  background-color: gray;
  margin: 5px;
  border-radius: 50%;
}

#traffic.stop li:nth-child(1) {
  background-color: #a00;
}

#traffic.wait li:nth-child(2) {
  background-color: #aa0;
}

#traffic.pass li:nth-child(3) {
  background-color: #0a0;
}
Copy the code

js

const traffic = document.getElementById('traffic'); Function wait(time){return new Promise(resolve => setTimeout(resolve, time)); Function setState(state){traffic. ClassName = state; } async function start(){ //noprotect while(1){ setState('wait'); await wait(1000); setState('stop'); await wait(3000); setState('pass'); await wait(3000); } } start();Copy the code

The setTimeOut method is wrapped as a wait function, which wraps the setTimeOut method with a promise and returns the Promise object.

With this wait function, the somewhat obscure nesting of setTimeOut can easily be rewritten into an await loop in an async function.