11521 sujets

JavaScript, DOM et API Web HTML5

Bonjour, pour un projet j'aurais besoins d'utiliser une fonction setTimeout() mais qui soit capable "d'attendre" un délais inférieur à 4ms ce qui j'ai cru comprendre n'étais pas possible en utilisant cette fonction. J'ai quelque chose du style:

for(let el of arr) {
    setTimeout(() => {
       div.style.height = el 
    }, delay);
}

J'ai essayé d'utiliser une fonction sleep() que j'ai trouvé sur stackoverflow mais plutôt que la div change successivement de taille, la height de la div passe directement du premier état au dernier avec un long délais entre les deux. La fonction sleep() que j'ai essayé:

function sleep(ms) {
    const now = Date.now();
    const limit = now + ms;
    let execute = true;
    while (execute) {
        if (limit < Date.now()) {
            execute = false;
        }
    }
    return;
  }

Est-ce qu'il y aurait une solution?
Merci beaucoup de votre temps!
Hello,

Au vu de ton code source, j'ai l'impression que tu essaies d'animer le changement de hauteur d'un div. As-tu essayer de plutôt passer par une transition CSS : https://www.alsacreations.com/tuto/lire/873-transitions-css3-animations.html ?

Exemple de fonctionnement :

.monDiv {
    transition: height 0.5s
}



// Récupération de l'élément qu'on veut modifier
monDiv = document.querySelector(".monDiv")
// Modification de la hauteur. Le changement sera animé d'après les paramètres de la transition CSS
monDiv.style.height = "200px"

Modifié par GuillaumeBauer (09 Feb 2022 - 15:57)
Merci beaucoup de ta proposition, en fait je n'essaye pas d'animer une div même si tout porte à le croire...
Pour mieux expliquer, je génère un nombre n de barres de tailles différentes les unes à côté des autres et grâce à différents algorithmes les barres s'ordonnes visuellement selon leurs tailles. Par simplicité, les barres n'échangent pas de places mais échange de tailles pour donner l'impression d'un déplacement. Seulement, il faut souvent compter au moins n**2 opérations pour complètement ordonner une liste de n élément et avec un délais de 4ms entre chaque opérations, la fonction met un temps fou (si j'ai 100 éléments je dois attendre 40s au minimum).
J'essaye donc de trouver un moyen de réduire ce délais.
Encore merci de ta proposition!
Modérateur
vzytoi a écrit :
Par simplicité, les barres n'échangent pas de places mais échange de tailles pour donner l'impression d'un déplacement.

Du coup c'est ptetre une autre solution qu'il faut choisir Smiley lol

Ou alors prendre un calcul moins couteux (si ca existe je ne suis pas un spécialiste et je veux pas y mettre le nez) ?

vzytoi a écrit :
Seulement, il faut souvent compter au moins n**2 opérations pour complètement ordonner une liste de n élément et avec un délais de 4ms entre chaque opérations, la fonction met un temps fou (si j'ai 100 éléments je dois attendre 40s au minimum).

Ca veut dire que meme avec un délai de 1ms il faudra attendre 10s en théorie ?
Modifié par _laurent (09 Feb 2022 - 14:33)
Modérateur
Bonjour,

Si tu n'as que 100 éléments à ordonner, ça prend une fraction de seconde (c'est quelques millisecondes). Ton problème est ailleurs (dans ton algorithme ? dans les opérations secondaires que tu fais au passage ?). Mais il nous faudrait plus de détails sur ce que tu fais pour qu'on puisse t'aider.

Amicalement,

EDIT: la "complexité" d'un algorithme de tri un peu optimisé est n*log(n) et non pas n**2. Cela veut dire que pour trier 100 éléments, il faut faire de l'ordre de quelques centaines de comparaisons et non pas 100*100 = 10000.

EDIT2: ceci étant, il n'y a pas que la complexité de l'algorithme qui compte, mais aussi la quantité de mémoire nécessaire, car celle-ci prend du temps à être allouée. Des algorithmes peuvent avoir besoin de peu d'opérations mais de beaucoup de mémoire et au final ne seront pas forcément plus rapides.

EDIT 3: un point important aussi est le nombre d'opérations élémentaires qu'il faut faire pour une comparaison. Si on a besoin d'une longue ligne de calcul pour faire la comparaison, on ira évidement moins vite.
Modifié par parsimonhi (09 Feb 2022 - 15:23)
Re-bonjour,

Je crois que j'ai compris : tu essaies de faire une animation de visualisation d'algorithmes de tri.

Si tel est le cas, je pense que la meilleure approche est de calculer dans un premier temps les étapes du tri et de les stocker dans une tableau :

// Pseudo-code
// Prend en argument un tableau contenant les valeurs à trier
// ainsi qu'une fonction calculant une étape du tri
// Renvoie un tableau contenant les étapes de tri
function generateSortingSteps(arr, sortStep) {
    // Le tableau initial passé en paramètre est la première étape de tri
    let steps = [...arr]
    while (<condition de tri>) {
        // Calcul de la prochaine étape du tri et stockage dans le tableau d'étapes
        // On passe l'étape précédente en paramètre de la fonction de tri
        steps.push(sortStep(steps.at(-1))
    }
    return steps
}


Une fois que cette matrice d'étapes est générée, on peut l'utiliser à notre guise pour la visualisation.

Pour cela, je te conseille de voir du côté de canvas : https://www.alsacreations.com/tuto/lire/1484-introduction.html ainsi que de la fonction requestAnimationFrame : https://developer.mozilla.org/fr/docs/Web/API/Canvas_API/Tutorial/Basic_animations. Tu pourras ainsi gérer toi-même la manière dont tes barres sont rendues dans le navigateur ainsi que leur animation, avec des timings beaucoup plus précis que si tu utilisais setTimeout.
Merci beaucoup pour vos messages et à ceux qui prendrons le temps le répondre à celui-ci
Mon problème est que il faut évidement que dans les algorithmes je marques des "pauses" pour que le changement visuelle soit observable, l'utilisateur étant dans la possibilité de choisir le nombre de barres, la vitesse (c'est à dire la durée de la pause à chaque affectation) et l'algorithme à utiliser.
Comment mon code fonctionne pour le moment est que la pose est effectuée à chaque affectation (ce que j'entend par affectation est changement de la height de la div) ce qui fait que les algorithmes qui reposent sur un très grand nombres de comparaisons comme le tri à bulles sont représenté à une vitesse bien supérieur comparée aux autres qui reposent sur un nombre plus faible d'opérations mais ont une complexité similaire.
Pour rétablir un équilibre et que la durée de la représentation visuelle soit plus proportionnée à la complexité réel de l'algorithme pour le nombre de barre en question il faudrait que je marque la pose sur les comparaison en plus des affectations ce qui représenterais un nombre de pauses très importantes au final.
Mon dilemme et donc de soit conserver une durée acceptable de visualisation sans prendre en compte le nombre de comparaisons, soit de marquer une pause à chaque comparaisons pour rétablir un équilibre sur l'efficacité de certains algorithme mais d'avoir une durée de visualisation beaucoup plus longue (d'après mes tests). Ou bien, c'est pour ça que j'ai posé cette question, trouver un moyen de marquer des pauses très rapides pour me permettre d'en marquer aux comparaisons et opérations sans rendre la visualisation trop longue.
Je ne suis pas sur de mettre fait vraiment comprendre, merci beaucoup de vos messages!

Le code est pas nécessaire pour mon explications mais si celle-ci n'est pas très claire ce sera peut-être mieux avec.... (pour ceux qui liront si vous avez des critiques par la même occasion je suis preneur)
Je génère les barres :

class Bars {
    constructor(container, nb) {
        this.container = container;
        this.nb = nb;
    }

    element(height, bnb) {
        let div = document.createElement('div');
        div.style.height = (height * 100) / bnb + '%';
        this.container.appendChild(div);
    }

    set(nb = null) {
        let h = [],
            bnb = nb === null ? this.nb : nb;
        for (let i = 0; i < bnb; i++) {
            let n;
            do {
                n = Math.floor(Math.random() * bnb) + 1;
            } while (h.includes(n));
            h.push(n);
            this.element(n, bnb);
        }
        return this.container.children;
    }

    clear() {
        while (this.container.firstChild) {
            this.container.firstChild.remove();
        }
    }

    reload(nb = null) {
        this.clear();
        this.set(nb);
    }
}
// range.value représente le nombre de barres.
const B = new Bars(container, range.value),
    bars = B.set();



Je trie :

class Algo {
    static async selectionSort() {
        let m;
        for (let i = 0; i < bars.length; i++) {
            m = i;
            for (let j = i + 1; j < bars.length; j++) {
                if (Sorting.compare(bars[m], bars[j], true)) {
                    m = j;
                }
            }
            await Sorting.replace(bars[i], bars[m], true);
        }
    }
}

Les fonctions que j'utilise pour ne pas trop me répéter parmi les différents algorithmes:

class Sorting {
    static color(bars, color) {
        for (let b of bars) {
            if (b.style) {
                b.style.background = color;
            }
        }
    }

    static calcSpeed() {
        return Math.abs(speed.value - speed.max);
    }

    static sleep(ms = null) {
        if (ms === null) {
            ms = this.calcSpeed();
        }
        return new Promise((resolve) => {
            setTimeout(resolve, ms);
        });
    }

    static async replace(a, b, both) {
        this.color([a, b], 'red');
        if (both) {
            [a.style.height, b.style.height] = [b.style.height, a.style.height];
        } else {
            if (b.style) {
                a.style.height = b.style.height;
            } else {
                a.style.height = b;
            }
        }
        await this.sleep();
        this.color([a, b], 'grey');
    }

    static async compare(a, b, strict) {
        if (a.style) a = a.style.height;
        if (b.style) b = b.style.height;
        if (strict) {
            return parseFloat(a) > parseFloat(b);
        } else {
            return parseFloat(a) >= parseFloat(b);
        }
    }
}
Modérateur
Bonjour,

1) Il marche cet algo ?

2) Les algos de tri doivent être exécutés entièrement séquentiellement car ils utilisent les résultats des pas précédents à chaque nouveau pas. Il ne devrait y avoir aucun async dans ce code, sauf peut-être le async selectionSort() initial, mais je ne vois pas trop l'utilité.

3) Les algos de tri sont rapides si tu ne fais que trier. C'est quand on fait des centaines de milliers de comparaison que ça commence à ralentir vraiment la machine. Mais évidemment, si tu fais un "reflow" de la page à chaque "pas", ça peut prendre du temps. Que tu changes la hauteur des barres ou que tu intervertisses la position des barres, ça ne devrait pas changer grand chose à mon avis (à tester quand même). C'est la mise à jour de l'affichage qui coutera le plus dans les deux cas.

4) Le délai (le 2e paramètre) de setTimeout peut être inférieur à 4ms, bien qu'effectivement les navigateurs attendront au moins 4ms entre deux setTimeout() imbriqués (c'est à dire quand le setTimeout() suivant est déclenché à l'intérieur de la fonction spécifiée en 1er paramètre du setTimeout précédent). Mais ils n'attendront pas les 4ms si les setTimeout() ne sont pas imbriqués. S'ils sont déclenchés via une boucle, il n'y aura pas ce délai minimal de 4ms. Par exemple, le code suivant sera exécuté en environ 1s et non pas en 4s :
let zero=Date.now();
for(let k=1;k<=1000;k++)
{
	setTimeout(function(){console.log(k+": "+(Date.now()-zero));},k);
}
Tandis que le code suivant sera exécuté en environ 4s :
let zero=Date.now(),k=0;
function oneMore() {
	k++;
	console.log(k+": "+(Date.now()-zero));
	if(k<1000) setTimeout(oneMore,1);
}
setTimeout(oneMore,1);
Il faut bien faire la différence entre les setTimeout() imbriqués et les setTimeout() séquentiels.

5) La proposition de GuillaumeBauer qui consiste à faire le tri dans un tableau, puis l'affichage (sous forme d'animation ou d'une série de setTimeout() si tu veux) une fois le tri terminé me parait pas mal vu que le rafraichissement de l'affichage est considérablement plus long que la comparaison pour le tri.

Amicalement,
Modifié par parsimonhi (09 Feb 2022 - 21:28)
Modérateur
Et l'eau,

Ce qui serait intéressant, il faudrait que tu ouvres un petit codepen afin de voir le contexte et t'aider à optimiser le code.