web-dev-qa-db-fra.com

Pourquoi setTimeout () "casse" pour les grandes valeurs de retard en millisecondes?

Je suis tombé sur un comportement inattendu lors du passage d'une grande valeur en millisecondes à setTimeout(). Par exemple,

setTimeout(some_callback, Number.MAX_VALUE);

et

setTimeout(some_callback, Infinity);

les deux causent some_callback à exécuter presque immédiatement, comme si j'avais réussi 0 au lieu d'un grand nombre comme délai.

Pourquoi cela arrive-t-il?

91
Matt Ball

Cela est dû au fait que setTimeout utilise un int 32 bits pour stocker le retard, de sorte que la valeur maximale autorisée serait

2147483647

si tu essayes

2147483648

vous obtenez votre problème.

Je peux seulement présumer que cela provoque une forme d'exception interne dans le moteur JS et provoque le déclenchement immédiat de la fonction plutôt que pas du tout.

127
OneSHOT

Quelques explications ici: http://closure-library.googlecode.com/svn/docs/closure_goog_timer_timer.js.source.html

Les valeurs de délai d'attente trop grandes pour tenir dans un entier 32 bits signé peuvent provoquer un débordement dans FF, Safari et Chrome, entraînant la planification immédiate du délai d'attente. Il est plus logique de ne pas planifier ces délais, car 24,8 jours dépassent une attente raisonnable pour que le navigateur reste ouvert.

21
warpech

Vous pouvez utiliser:

function runAtDate(date, func) {
    var now = (new Date()).getTime();
    var then = date.getTime();
    var diff = Math.max((then - now), 0);
    if (diff > 0x7FFFFFFF) //setTimeout limit is MAX_INT32=(2^31-1)
        setTimeout(function() {runAtDate(date, func);}, 0x7FFFFFFF);
    else
        setTimeout(func, diff);
}
17
Ronen

Je suis tombé dessus lorsque j'ai essayé de déconnecter automatiquement un utilisateur avec une session expirée. Ma solution était de simplement réinitialiser le délai d'attente après une journée et de conserver la fonctionnalité pour utiliser clearTimeout.

Voici un petit exemple de prototype:

Timer = function(execTime, callback) {
    if(!(execTime instanceof Date)) {
        execTime = new Date(execTime);
    }

    this.execTime = execTime;
    this.callback = callback;

    this.init();
};

Timer.prototype = {

    callback: null,
    execTime: null,

    _timeout : null,

    /**
     * Initialize and start timer
     */
    init : function() {
        this.checkTimer();
    },

    /**
     * Get the time of the callback execution should happen
     */
    getExecTime : function() {
        return this.execTime;
    },

    /**
     * Checks the current time with the execute time and executes callback accordingly
     */
    checkTimer : function() {
        clearTimeout(this._timeout);

        var now = new Date();
        var ms = this.getExecTime().getTime() - now.getTime();

        /**
         * Check if timer has expired
         */
        if(ms <= 0) {
            this.callback(this);

            return false;
        }

        /**
         * Check if ms is more than one day, then revered to one day
         */
        var max = (86400 * 1000);
        if(ms > max) {
            ms = max;
        }

        /**
         * Otherwise set timeout
         */
        this._timeout = setTimeout(function(self) {
            self.checkTimer();
        }, ms, this);
    },

    /**
     * Stops the timeout
     */
    stopTimer : function() {
        clearTimeout(this._timeout);
    }
};

Usage:

var timer = new Timer('2018-08-17 14:05:00', function() {
    document.location.reload();
});

Et vous pouvez l'effacer avec la méthode stopTimer:

timer.stopTimer();
1
Tim

Consultez la documentation du nœud sur les temporisateurs ici: https://nodejs.org/api/timers.html (en supposant la même chose sur js également car c'est un terme omniprésent maintenant dans la boucle d'événements basée sur

En bref:

Lorsque le retard est supérieur à 2147483647 ou inférieur à 1, le retard sera réglé sur 1.

et le retard est:

Le nombre de millisecondes à attendre avant d'appeler le rappel.

On dirait que votre valeur de délai d'attente est par défaut une valeur inattendue le long de ces règles, peut-être?

1
SillyGilly

Je ne peux pas commenter mais répondre à toutes les personnes. Cela prend une valeur non signée (vous ne pouvez évidemment pas attendre des millisecondes négatives) Donc, comme la valeur maximale est "2147483647" lorsque vous entrez une valeur plus élevée, elle commence à passer de 0.

Retard essentiellement = {VALUE}% 2147483647.

Donc, en utilisant un retard de 2147483648, cela ferait 1 milliseconde, donc, proc instantané.

0
KYGAS