Estoy tratando de iniciar y detener un temporizador con un setTimeout recursivo, ya que setInterval no se ajusta a mis necesidades. Cuando el usuario presiona iniciar, el temporizador comienza a contar.
1...2...3...4..5...1...2...3..4...5...1..2...3..4. ..5...
Y cuando el usuario presiona detener, debería detener este bucle de temporizador. Si presiono iniciar nuevamente, cambiando el estado del botón (variable de flujo), el temporizador debería hacer lo mismo nuevamente (1..2..3..4..5..1..2...3. .4...5..)
Pensé que la forma de hacerlo sería esta.
const ref = useRef(null); useEffect(() => { if (!stream) { ref.current=setTimeout(repeatingFunc, 1000); } else { clearTimeout(ref.current); } }, [stream]); function repeatingFunc() { console.log("It's been 5 seconds. Execute the function again."); setTimeout(repeatingFunc, 5000); }¿Qué estoy haciendo mal?
El temporizador no se detiene. ¡Continúa incluso si presiono detener! (¡cambiando el valor del estado de la transmisión!)
Puede definir un temporizador personalizado como este:
function CustomTimer(func) { this.id = -1; this.func = func; this.start = function (interval) { this.stop(); if (this.func) { var t = this; this.id = setTimeout(function () { t.func.call(t) }, interval || 1000); } } this.stop = function () { if (this.id >= 0) { clearTimeout(this.id); this.id = -1; } } }Y úsalo:
var myTimer = new CustomTimer(function () { console.log("It's been 5 seconds. Execute the function again."); this.start(5000); }); myTimer.start(5000);Puedes cambiar la función a ejecutar en cualquier momento y también el intervalo. Por ejemplo: ejecute un evento de inicio en 1 segundo y luego, 5 veces cada 5 segundos haga otra cosa:
var secondFunc = function () { console.log("It's been 5 seconds. Execute the function again."); if (++this.count < 5) this.start(5000); } var myTimer = new CustomTimer(function () { console.log("First event at 1 second"); this.count = 0; this.func = secondFunc; this.start(5000); }); myTimer.start(1000);La razón por la que el temporizador no se detiene es porque solo almacena el ID de tiempo de espera de la llamada setTimeout inicial. Cuando se llama a repeatingFunc , se registra otra devolución de llamada setTimeout , que tiene una nueva ID de tiempo de espera.
Entonces, cuando intenta borrar el tiempo de espera usando clearTimeout(ref.current) , está pasando una ID de tiempo de espera desactualizada.
Dado que ya se ha llamado al registro anterior setTimeout , ya no necesita almacenar la ID anterior y simplemente puede reemplazarla por la nueva.
const ref = useRef(null); useEffect(() => { if (!stream) { ref.current = setTimeout(repeatingFunc, 1000); } else { clearTimeout(ref.current); } }, [stream]); function repeatingFunc() { console.log("It's been 5 seconds. Execute the function again."); ref.current = setTimeout(repeatingFunc, 5000); // ^^^^^^^^^^^^ update timeout ID after registering a new timeout } Tenga en cuenta que el alcance de repeatingFunc se fija en un punto en el tiempo en el que inicialmente llama a setTimeout(repeatingFunc, 1000) , lo que significa que cosas como estados y propiedades pueden contener valores obsoletos. Esto podría o no convertirse en un problema dependiendo de su contexto.
También es posible que desee agregar una función de limpieza useEffect que borre el tiempo de espera en el desmontaje del componente. De lo contrario, su ciclo continúa, incluso cuando el componente ya no está montado.
useEffect(() => { if (!stream) { ref.current = setTimeout(repeatingFunc, 1000); } return () => clearTimeout(ref.current); }, [stream]); Suponiendo que la stream es un valor booleano ( true / false ), lo anterior borrará el tiempo de espera actual cada vez que se cambie el valor de la stream o cuando se desmonte el componente.
Cuando la stream cambia de true a false , se llamará a la función de limpieza y se borrará el tiempo de espera.
Cuando la stream cambia de false a true , también se llama a la función de limpieza, pero no funciona porque la referencia actual ref.current no está registrada. Entonces se establece un nuevo tiempo de espera.
Pasar una ID no válida a
clearTimeout()no hace nada en silencio; no se lanza ninguna excepción.
Ver: MDN - clearTimeout()