Problema con un setInterval e 2 setTimeout annidati

di il
8 risposte

Problema con un setInterval e 2 setTimeout annidati

Salve.
Ho un problema che credo sia riferibile ai miei limiti di comprensione del funzionamento sincrono/asincrono di js.
Allora, sto lavorando su una piccola struttura, e con questo codice, tutto funziona correttamente.

let timeOut, timeOut2;
let slides = document.querySelectorAll('#due .slideshow__inner');
over = 1;
let back = 0;
timeOut = setTimeout(function () {
  change();
}, 8000);

function change() {
  innerFront.querySelectorAll('figure')[0].classList.add('isActive');
  timeOut2 = setTimeout(function () {
    innerBack.querySelectorAll('figure')[0].classList.add('isActive');
  }, 1000);
}
Se modifico il codice per gestire più elementi, invece, il setTimeout assegnato alla variabile timeOut2 viene eseguito non in sequenza dopo l'assegnazione nella funzione change, ma solo alla fine delle operazioni.
Questo il codice modificato:

setInterval(function () {
  innerFront = slides[over];
  innerBack = slides[back];
  change();
  innerFront.classList.remove('slideshow__inner--front');
  innerFront.querySelectorAll('figure')[0].classList.remove('isActive');
  innerBack.querySelectorAll('figure')[0].classList.remove('isActive');
  innerBack.classList.remove('slideshow__inner--back');
  over -= 1;
  if (over < 0) over = slides.length - 1;
  slides[over].classList.add('slideshow__inner--front');
  back = over - 1;
  if (back < 0) back = slides.length - 1;
  slides[back].classList.add('slideshow__inner--back');
}, 8000);

function change() {
  innerFront.querySelectorAll('figure')[0].classList.add('isActive');
  timeOut2 = setTimeout(function () {
    innerBack.querySelectorAll('figure')[0].classList.add('isActive');
  }, 1000);
}
Per la cronaca, questa è la struttura HTML interessata:

    <div class="container" id="due">
      <h1>Secondo metodo</h1>
      <div class="slideshow">
        <div class="slideshow__inner slideshow__inner--back">
            <figure style="background-image: url('05-img/img1.jpg')"info="1"></figure>
        </div>
        <div class="slideshow__inner slideshow__inner--front">
            <figure style="background-image: url('05-img/img2.jpg')"info="2"></figure>
        </div>
       </div>
    </div>
Qualcuno sa spiegarmi il motivo, e come correggere l'errore?
Grazie.
[/code]

8 Risposte

  • Re: Problema con un setInterval e 2 setTimeout annidati

    BennyBatt ha scritto:


    Se modifico il codice per gestire più elementi, invece, il setTimeout assegnato alla variabile timeOut2 viene eseguito non in sequenza dopo l'assegnazione nella funzione change, ma solo alla fine delle operazioni. [...]
    Qualcuno sa spiegarmi il motivo, e come correggere l'errore?
    Il quesito e il codice mi sono tutto fuorché chiari.

    Cosa si intende per "solo alla fine delle operazioni"?
    Qual è l'effetto che vuoi ottenere sugli elementi della pagina?
    E perché combinare un setInterval con un setTimeout?

    Scrivi meglio il codice (ad esempio, stai ripetendo sempre per esteso innerFront.querySelectorAll('figure')[0].classList quando basterebbe usare una variabile e rifattorizzare la procedura), così che si possa capire l'algoritmo e descrivi in modo più dettagliato cosa stai cercando di ottenere di preciso.

    Ciao!
  • Re: Problema con un setInterval e 2 setTimeout annidati

    Intanto, grazie per la risposta.
    Allora, quello che cerco di realizzare è uno slider, usando JS e CSS3.
    Tralascio la parte CSS perché non è quella che pone problemi (nel caso servisse, posso scrivere un codepen).
    In pratica, vorrei che le varie img contenute nei DIV di classe slideshow__inner si sussegguissero cambiando il loro stato.
    Nel primo esempio, dove ci sono solo 2 immagini, manca la definizione iniziale delle 2 variabili di lavoro (legate ai CSS), e c'è un errore. Riporto il codice corretto:
    
    let timeOut, timeOut2;
    // ! funzioni secondo metodo
    
    let innerFront = document.querySelectorAll('#due .slideshow__inner--front');
    let innerBack = document.querySelectorAll('#due .slideshow__inner--back');
    window.onload = function () {
      timeOut = setTimeout(function () {
        change();
      }, 8000);
    }
    function change() {
      innerFront[0].querySelectorAll('figure')[0].classList.add('isActive');
      timeOut2 = setTimeout(function () {
        innerBack[0].querySelectorAll('figure')[0].classList.add('isActive');
      }, 1000);
    });
    Mi scuso, ma la fretta...
    In ogni modo, accade che dopo 8 secondi (primo setTimeout), nel DIV della variabile innerBack viene aggiunta la classe isActive, che fa partire l'animazione innerBack.isActive impostata nei CSS.
    All'interno, è posto un secondo setTimeout che dopo 1 secondo aggiunge nel nel DIV della variabile innerFront un'altra classe isActive, che fa partire l'animazione innerFront.isActive impostata nei CSS.
    Con questa impostazione, tutto funziona bene, nel senso che la sequenza delle azioni è corretta.

    Il problema sorge quando io voglio rendere il ciclo infinito, cioè ripetitivo, e magari far scorrere altre img.
    Quindi, al posto del primo setTimeout, pongo un setInterval, e poi gestisco lo spostamento delle denominazioni dei DIV in sequenza, cioè sposto le classi slideshow__inner--front e slideshow__inner--back, e tolgo le classi isActive, per poi continuare a metterle in sequenza.
    Accade che, con il mio codice, la prima classe isActive viene correttamente impostata nella <figure> della classe slideshow__inner--front, mentre l'altra per la classe slideshow__inner--back, contenuta nella function 'change', no.
    Non so se ciò chiarisce, spero di sì.
    Diciamo che sono un po' impantanato, ed è vero che il codice dovrebbe essere più pulito e leggibile, però temo che il problema sia concettuale.
    Ripeto, se occorre uno snippet codepen o altro, lo predispongo.
  • Re: Problema con un setInterval e 2 setTimeout annidati

    Allora, per meglio spiegare, aggiungo uno snippet: .
    Questa l'operazione singola, appunto, che mostra la corretta sequenza delle operazioni (ciclo singolo).
    Vorrei trasformare il setTimeou in setInterval, e rendere ciclica l'operazione anche su più immagini (quindi su più DIV con classe slideshow__inner).
    Il mio tentativo è quello riportato in quest'altro snippet: , con i relativi errori. In pratica, il setTimeout nella function change viene eseguito dopo le istruzioni che spostano i riferimenti delle classi nella function chiamante.

    Spero sia più chiaro.
    E spero che qualcuno sappia aiutarmi.
  • Re: Problema con un setInterval e 2 setTimeout annidati

    BennyBatt ha scritto:


    Vorrei trasformare il setTimeou in setInterval, e rendere ciclica l'operazione anche su più immagini (quindi su più DIV con classe slideshow__inner).
    Ricorda che un setTimeout può sempre impostare un nuovo setTimeout, creando di fatto lo stesso effetto di un setInterval, ma con la possibilità di controllarlo meglio.

    Ad ogni modo, io cerco sempre di evitare timeout/intervalli che si innescano l'uno dall'altro o che continuano in modo indefinito: se proprio devo eseguire qualcosa di temporizzato, creo un unico intervallo con setInterval e all'interno mi appoggio ad altre variabili per controllare qual è la situazione (ossia lo stato attuale degli elementi su cui interagire) e impostare di conseguenza la nuova presentazione.

    Esemplificando, se devo fare una cosa dopo un secondo e un'altra dopo otto secondi, quello che faccio è usare un unico intervallo in cui verifico il tempo trascorso o mantengo un conteggio affinché sia possibile sapere in quale "step temporale" mi trovo e come reagire di conseguenza... in questo modo, il tutto avviene solamente usando un "temporizzatore" senza complicarsi troppo la vita.

    Proverei a semplificare l'implementazione in questo senso e sicuramente risulterà meglio verificabile e controllabile.

    Ciao!
  • Re: Problema con un setInterval e 2 setTimeout annidati

    Come ho riportato sopra, ti mostro il mio tentativo in quest'altro snippet: , con i relativi errori. In pratica, il setTimeout nella function change viene eseguito dopo le istruzioni che spostano i riferimenti delle classi nella function chiamante.

    Al momento mi piacerebbe sapere dove sbaglio, per un chiarimento concettuale. Se hai la pazienza di dare un'occhiata al jsfiddle. Il ciclo funziona abbastanza bene, ma appunto salta un passaggio. Mi sto arrovellando da un paio di giorni, ho pure provato a introdurre una async/await con promise per gestire il settTmeout nella funzione change, ma senza successo. Capire il difetto mi aiuterebbe a comprendere meglio il meccanismo.
    Comunque sia, rifletterò anche sul tuo suggerimento.

    Fammi sapere, se puoi.

    Grazie.
  • Re: Problema con un setInterval e 2 setTimeout annidati

    BennyBatt ha scritto:


    Se hai la pazienza di dare un'occhiata al jsfiddle. [...]
    Il problema è che tra un querySelectorAll() e l'altro, un setInterval() che "accavalla" un setTimeout() chiamando un change() con vari console.log(), non avendo nemmeno ben chiaro l'effetto *reale* che si vuole ottenere alla fine, e non avendo tempo utile per approfondire markup e stili... non capisco una mazza del codice.
  • Re: Problema con un setInterval e 2 setTimeout annidati

    Ok, forse devo essere più chiaro.
    Allora, guardando il 1° jsfiddle, io ho nel container 2 DIV con classe slideshow__inner, che contengono 2 figure in ognuno dei quali c'è una img.
    Al primo DIV.slideshow__inner aggiungo una classe slideshow__inner--back perché è l'immagine che sta sotto (quindi, ha z-index inferiore) e che prende un effetto blur.
    Al secondo DIV.slideshow__inner aggiungo una classe slideshow__inner--front perché è l'immagine che sta sopra, e alla quale pongo uno z-index più alto.

    Quando attuo l'operazione, applico al figure della DIV con classe slideshow__inner--front la classe isActive, che aggiunge una animation di 2 secondi che riduce con un effetto grafico width e height fino a portarle a 0.
    Dacche, sotto resta la DIV con classe slideshow__inner--back e l'effetto blur.
    L'idea è che al termine della animation (dopo 2 secondi, inseriti con un altro setTimeout) venga applicata alla figure della DIV con classe slideshow__inner--back ancora una classe isActive, che riporta l'effetto blur a 0 con una transition.
    Nel jsfiddle pare funzionare correttamente.
    Il problema sorge quando io voglio creare un effetto ciclico, che consisterebbe nelle mie intenzioni di rimuovere progressivamente e in sequenza le classi slideshow__inner--front, slideshow__inner--back e isActive, rimettendole nelle DIV nell'iterazione temporale successiva.
    Putroppo non ho familiarità con i temporizzatori JS, e forse la strada che ho intrapreso è troppo astrusa. Non so.
    Ho provato in altri modi, ma l'istruzione temporizzata che vorrebbe applicare la classe isActive alla figure della DIV con classe slideshow__inner--back non si verifica mai (viene sempre applicato prima il remove, credo).

    Non so se così è più chiaro. Forse no.
  • Re: Problema con un setInterval e 2 setTimeout annidati

    Allora, ho trovato una soluzione.
    In pratica, come sospettavo, la chiamata alla function change non interrompeva il codice per il tempo sufficiente all'attuazione della transition impostata applicando la classe isActive alla DIV con classe slideshow__inner--back.
    Quindi, ho pensato di mettere in attesa la parte successiva del codice. Dopo alcuni tentativi, sono riuscito introducendo una function async e una promise (function sleep), oltre ad un'altra function di attesa anch'essa contentente una promise (function timeout). Ora il ciclo è corretto.
    Diciamo che la transition da blur(8px) a blur(0px) non è entusiasmante (credo dipenda dalla routine del browser, ma forse mi sbaglio), però il ciclo funziona.
    Nella JSfiddle allego la routine, se a qualcuno interessasse, o se qualcuno volesse dare suggerimenti.
Devi accedere o registrarti per scrivere nel forum
8 risposte