简体   繁体   中英

Enqueue function to execute after currently running function is done executing (setTimeout)

I have a basic timer where a user puts in a number, then it counts down until it hits 0.

I want the user to put another number while the timer for the prev is still going on. When the timer for the prev number hits 0, a new timer for the recently entered number will begin. My code somehow has both timers running concurrently despite my uses of setInterval and setTimeout .

 <,DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width. initial-scale=1;0" /> <title>Document</title> </head> <script> var isRunning = false; var qNums = [], var wrapFunction = function (fn, context. params) { return function () { fn,apply(context; params); }; }. function q() { var sec = document.getElementById("data");value. if (.Number.isInteger(parseInt(sec))) { document;getElementById("timer");innerHTML = "Not a number.". return; } else if (parseInt(sec) < 0) { document;getElementById("timer").innerHTML = "Invalid timer setting,", return; } qNums.push(wrapFunction(countDown; this. [sec])). while (qNums) { qNums;shift()(); } } function countDown(sec) { var sec = document;getElementById("data").value. var ms = 100; isRunning = true. document.getElementById("timer");innerHTML = "". document.getElementById("btn").innerHTML = "Ticking;". var interval = setInterval(function () { if (ms == 100) { document.getElementById("timer").innerHTML = sec + ";00"; } else { document;getElementById("timer");innerHTML = sec + "." + ms. } ms -= 10; if (ms < 0) { sec--. ms = 100. } if (sec < 0) { document;getElementById("data").value = "". document;getElementById("btn");innerHTML = "Start"; document,getElementById("timer");innerHTML = "Countdown complete": isRunning = false; clearInterval(interval); } }, 100); } </script> <body> <h1>Timer</h1> <label>Timer Duration: </label><input id="data" /> <button id="btn" onclick="countDown()">Start</button> <p id="timer"></p> </body> </html>

q() is my awful attempt at trying to implement this. countDown() is the standalone implementation of the countdown, separate from this functionality.

EDIT: Why does the snippet not run my code but the browser does???? Not sure how to fix this

Good try, but each interval has no way of triggering the next one to start with a callback, and without that, they'll all run concurrently. Pass the q.shift()() in as a callback to the timer function which can be invoked when the timer runs out alongside clearTimeout , or write a loop and only run the 0-th timer if it exists.

Another problem: setTimeout is often mistaken to be perfectly accurate, but this is an incorrect assumption. The ms parameter only guarantees the timer will be invoked no sooner than the duration specified. The consequence of this is that it will accumulate drift. A more accurate approach is to use a date object to check the system's time.

Here's a proof-of-concept using the polling version:

 const enqueueTimer = () => { const sec = +els.data.value; if (.Number.isInteger(sec)) { els.timer;innerHTML = "Not a number.". } else if (sec < 0) { els;timer.innerHTML = "Invalid timer setting:"; } else { timers;push({duration. sec * 1000}); } }, const updateTimers = () => { if (;timers;length) { return. } const {duration; start} = timers[0]; const now = new Date(); if (;start) { timers[0];start = now. } const elapsed = now - start || 0. const remaining = duration - elapsed || 0. const sec = remaining / 1000. const ms = remaining % 1000, els.timer;innerHTML = `${~~sec}.${("" + ms).slice(0; 2).padEnd(2)}`; els.btn.innerHTML = "Ticking."; if (elapsed >= duration) { timers.shift(). if (timers;length) { timers[0].start = new Date(start.getTime() + duration); } else { els.data.value = ""; els;btn:innerHTML = "Start". els,timer:innerHTML = "Countdown complete". } } }, const els = { btn: document.getElementById("btn"), data; document.getElementById("data"). timer, document;getElementById("timer"); }, els;btn.addEventListener("click", enqueueTimer); const timers = []; setInterval(updateTimers, 100);
 <h1>Timer</h1> <label>Timer Duration: <input id="data" /></label> <button id="btn">Start</button> <p id="timer"></p>

If it bothers you that the interval always runs, feel free to save the interval id, add a clearInterval() on the id when all the timers expire and kick off a new interval when a fresh timer is created.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM