JSFiddle - React, Tailwind, and code Playground
by lid0
HTML
<div id="app">
<div style="border:1px solid gray;padding:5px;border-radius:3px;margin-bottom:5px;">
1. click <kbd>"run/add tasks"</kbd> button once or many times, to start and add more tasks <br>
2. click <kbd>cancel</kbd> button, to abort all in progress tasks.<br>
3. <kbd>reset app()</kbd> to clear output and reset application state.
</div>
<div style="margin-top:15px;margin-bottom:15px;">
Status: <div id="status">
-
</div>
</div>
<div>
<button onClick="abort()">
cancel
</button>
<button onClick="run()">
RUN/ADD tasks
</button>
<progress id="progress" max=0 value=0></progress>
<output id="progressStats">0 / 0</output>
</div>
<hr>
<button onClick="resetApp()">
reset app ()
</button> clear output,and reset progress bar
<hr>
<div>
<div style="margin-top:5px;">
Output:
</div>
<pre id="result">
</pre>
</div>
</div>
CSS
#status {
border: 1px solid #cfcfcf;
border-radius: 3px;
padding: 3px;
display: inline-block;
min-width: 150px;
}
#app.aborted #status {
background: #ea5e5e;
}
#app.running #status {
background: #52c8ce;
}
#app.completed #status {
background: #80e606;
}
b.aborted {
color: red;
}
span.aborted {
color: red;
}
b.success {
color: darkgreen;
}
b span.success {
color: darkgreen;
}
#result {
border: 1px solid #000;
padding: 3px;
border-radius: 3px;
}
#app.aborted ::-moz-progress-bar {
background: #ea5e5e;
}
#app.running ::-moz-progress-bar {
background: #52c8ce;
}
#app.completed ::-moz-progress-bar {
background: #80e606;
}
JavaScript
// Author: lidlanca 2021 March 25
var cancelController = new AbortController(); // abort controller. can only be used once to signal abort
var requestPromises = []; // all ongoing request promises
// UI elements
var resultEl = document.getElementById("result")
var statusEl = document.getElementById("status")
var progressEl = document.getElementById("progress")
var progressStatsEl = document.getElementById("progressStats")
var appEl = document.getElementById("app")
// App State
var running = false
var aborted = false
var requestCounter = {
requestAbortedCount: 0,
requestFulfilled: 0,
requestScheduled: 0,
}
// mock fetch(), simulate random request time.
async function fetch(url, options) {
({
signal
} = options)
return new Promise(function(resolve, reject) {
signal.addEventListener('abort', () => {
// in a real fetch it would throw AbortError
reject(url + " aborted")
})
setTimeout(() => (resolve(url + " Completed Successfully")), 1000 * (Math.random() * 10 + 3))
})
}
// set the app container with a status class for styling.
function setAppActiveStatus(cls) {
var classes = ["running", "aborted", "completed"]
appEl.classList.remove(...classes)
appEl.classList.add(cls)
}
// abort any pending tasks, not yet completed in the current run.
function abort() {
if (!running) {
console.log("skipped abort called, no running tasks")
return
}
// trigger the abort signal.
cancelController.abort()
cancelController = new AbortController(); // create new instance controller, for the next run. not reusuable
// update app state flags.
aborted = true
running = false;
setAppActiveStatus("aborted")
}
// print message to the output box
function print(msg) {
if (msg.includes("aborted")) {
msg = "<b class='aborted'>" + msg + "</b>"
} else if (msg.includes("Completed Successfully")) {
msg = "<b class='success'>" + msg + "</b>"
}
resultEl.innerHTML += msg + "\n"
}
function...