We can split up your display and processing logic via a very simple implementation of the publish-subscribe pattern. We need basically threse things
- A message queue or message channel. In this case we could use an array - no reason not to, as it's the simplest to implement it.
- A publisher will add messages to the queue. In this case, the algorithm you do would be adding
board
every time.
- A subscriber that will do something with each entry in the array. In this case, simply print it to the console.
- A message broker - this is typically the part that sends the messages to subscribers. Here, we can use a simple
setInterval
that would do periodic polling of the message queue and if there are messages, would forward them.
A commercial publish-subscribe system is likely to have a lot of bells and whistles - multiple queues/channels, message filtering, topics, storage of consumed events, etc. We don't really need all of that for this case but it's useful to mention that the following is an example of how pub-sub systems work, rather than a full implementation. Still, we don't necessarily need all of that for this problem. Here is a rudimentary implementation:
//the message queue
let queue = [];
//our message broker
setInterval(
() => {
if (queue.length !== 0) {
const frame = queue.shift();
//our subscriber
print(frame);
}
},
1000
);
//our publisher
function calculate() {
let board = "";
for (let y = 0; y < 8; y++) {
for (let x = 0; x < 8; x++) {
if ((x + y) % 2 == 0)
{board += " ";}
else {board += "#";}
}
board += "\n";
queue.push(board); //the publish action
}
}
calculate();
//just print to the console
function print(frame) {
console.log(frame)
}
As mentioned, this is an overview of how this can work. Note that the implementation is closer to observer but doesn't act at the same moment as the change. At times design patterns may blur the lines between each other, especially depending on how they are used.
At any rate, the advantage of this pattern is that you can decouple your display (data consumption) logic to the calculations (data production). Your algorithm doesn't need to change if you want to add different consumers for the data it produces.
We can expand this to include multiple subscribers easily:
let queue = [];
let subscribers = [];
//add to a pool of subscribers that will all run on the same schedule and consume messages
function subscribe(callback) {
subscribers.push(callback);
}
setInterval(
() => {
if (queue.length !== 0) {
const frame = queue.shift();
subscribers.forEach(sub => sub(frame));
}
},
1000
);
function calculate() {
let board = "";
for (let y = 0; y < 8; y++) {
for (let x = 0; x < 8; x++) {
if ((x + y) % 2 == 0)
{board += " ";}
else {board += "#";}
}
board += "\n";
queue.push(board);
}
}
subscribe(print);
subscribe(displayHTML);
calculate();
//just print to the console
function print(frame) {
console.log(frame);
}
//show as HTML on the page
function displayHTML(frame) {
const displayElement = document.getElementById("display_area");
//convert newlines for HTML display
const displayFrame = frame
.replace(/\n/g, '<br/>') //new lines to <br/> tags
.replace(/ /g, ' '); //spaces to non-breakable spaces
displayElement.innerHTML = displayFrame;
}
.as-console-wrapper {
/* resize the console otherwise it covers up the HTML display */
left: 300px !important;
}
<div id="display_area"></div>
Or here is a minor variation where you can add subscribers that work at different schedules:
let queue = [];
//add different subscribers that might work at different times through the messages
function subscribe(subscriberCallback, pollTimeMs) {
let lastFrameIndex = 0;
setInterval(
() => {
if (queue.length !== lastFrameIndex) {
//next message in the queue for this subscriber
const frame = queue[lastFrameIndex++];
subscriberCallback(frame);
}
},
pollTimeMs
);
}
function calculate() {
let board = "";
for (let y = 0; y < 8; y++) {
for (let x = 0; x < 8; x++) {
if ((x + y) % 2 == 0)
{board += " ";}
else {board += "#";}
}
board += "\n";
queue.push(board);
}
}
subscribe(print, 1000);
subscribe(displayHTML, 1500);
calculate();
//just print to the console
function print(frame) {
console.log(frame);
}
//show as HTML on the page
function displayHTML(frame) {
const displayElement = document.getElementById("display_area");
//convert newlines for HTML display
const displayFrame = frame
.replace(/\n/g, '<br/>') //new lines to <br/> tags
.replace(/ /g, ' '); //spaces to non-breakable spaces
displayElement.innerHTML = displayFrame;
}
.as-console-wrapper {
/* resize the console otherwise it covers up the HTML display */
left: 300px !important;
}
<div id="display_area"></div>