3847 Stimmen

Was ist die JavaScript-Version von sleep()?

Gibt es einen besseren Weg zur Entwicklung einer sleep in JavaScript als die folgenden pausecomp Funktion ( entnommen von hier )?

function pausecomp(millis)
{
    var date = new Date();
    var curDate = null;
    do { curDate = new Date(); }
    while(curDate-date < millis);
}

Es handelt sich nicht um ein Duplikat von Sleep in JavaScript - Verzögerung zwischen Aktionen Ich möchte eine echter Schlaf in der Mitte einer Funktion und nicht eine Verzögerung, bevor ein Teil des Codes ausgeführt wird.

6voto

Ole Middelboe Punkte 11

Eine alte Frage aus dem Jahr 2009. Jetzt im Jahr 2015 ist eine neue Lösung mit Generatoren in ECMAScript 2015 AKA ES6 definiert möglich. Es wurde im Juni 2015 genehmigt, aber es wurde in Firefox und Chrome vor implementiert. Jetzt kann eine Sleep-Funktion nicht mehr blockiert und in Schleifen und Unterfunktionen verschachtelt werden, ohne dass der Browser einfriert. Es wird nur reines JavaScript benötigt - keine Bibliotheken oder Frameworks.

Das folgende Programm zeigt, wie sleep() y runSleepyTask() hergestellt werden können. Die sleep() Funktion ist nur eine yield Erklärung. Sie ist so einfach, dass es eigentlich einfacher ist, die yield Anweisung direkt anstelle des Aufrufs sleep() aber dann gäbe es kein sleep-Wort :-) Die Ausbeute gibt einen Zeitwert an die next() innere Methode wakeup() und wartet. Das eigentliche "Schlafen" findet statt in wakeup() unter Verwendung der guten alten setTimeout() . Beim Rückruf wird die next() Methode löst die yield Anweisung fortzusetzen, und die "Magie" von Yield besteht darin, dass alle lokalen Variablen und der gesamte Aufrufstapel um sie herum noch intakt sind.

Funktionen, die sleep() oder yield verwenden, müssen als Generatoren definiert werden. Dies ist einfach durch Hinzufügen eines Sternchens zum Schlüsselwort function* . Die Ausführung eines Generators ist etwas komplizierter. Beim Aufruf mit dem Schlüsselwort new gibt der Generator ein Objekt zurück, das die next() Methode, aber der Körper des Generators wird nicht ausgeführt (das Schlüsselwort new ist optional und macht keinen Unterschied). Die next() Methode löst die Ausführung des Generatorkörpers aus, bis sie auf eine yield . Die Wrapper-Funktion runSleepyTask() startet das Ping-Pong-Spiel: next() wartet auf eine yield et yield wartet ein next() .

Eine andere Möglichkeit, einen Generator aufzurufen, ist mit dem Schlüsselwort yield* Hier funktioniert es wie ein einfacher Funktionsaufruf, aber es beinhaltet auch die Möglichkeit der Rückgabe an next() .

Dies alles wird durch das folgende Beispiel veranschaulicht drawTree() . Es zeichnet einen Baum mit Blättern auf eine rotierende 3D-Szene. Ein Baum wird als Stamm mit 3 Teilen an der Spitze in verschiedenen Richtungen gezeichnet. Jedes Teil wird dann als ein anderer, kleinerer Baum gezeichnet, indem man drawTree() nach einem kurzen Schlaf rekursiv. Ein sehr kleiner Baum wird nur als ein Blatt gezeichnet.

Jedes Blatt hat sein eigenes Leben in einer separaten Aufgabe, die mit runSleepyTask() . Sie wird geboren, wächst, sitzt, verblasst, fällt und stirbt in growLeaf() . Die Geschwindigkeit wird kontrolliert mit sleep() . Dies zeigt, wie einfach Multitasking sein kann.

function* sleep(milliseconds) {yield milliseconds};

function runSleepyTask(task) {
    (function wakeup() {
        var result = task.next();
        if (!result.done) setTimeout(wakeup, result.value);
    })()
}
//////////////// written by Ole Middelboe  /////////////////////////////

pen3D =setup3D();
var taskObject = new drawTree(pen3D.center, 5);
runSleepyTask(taskObject);

function* drawTree(root3D, size) {
    if (size < 2) runSleepyTask(new growLeaf(root3D))
    else {
        pen3D.drawTrunk(root3D, size);
        for (var p of [1, 3, 5]) {
            var part3D = new pen3D.Thing;
            root3D.add(part3D);
            part3D.move(size).turn(p).tilt(1-p/20);
            yield* sleep(50);
            yield* drawTree(part3D, (0.7+p/40)*size);
        }
    }
}

function* growLeaf(stem3D) {
    var leaf3D = pen3D.drawLeaf(stem3D);
    for (var s=0;s++<15;) {yield* sleep(100); leaf3D.scale.multiplyScalar(1.1)}
    yield* sleep( 1000 + 9000*Math.random() );
    for (var c=0;c++<30;) {yield* sleep(200); leaf3D.skin.color.setRGB(c/30, 1-c/40, 0)}
    for (var m=0;m++<90;) {yield* sleep( 50); leaf3D.turn(0.4).tilt(0.3).move(2)}
    leaf3D.visible = false;
}
///////////////////////////////////////////////////////////////////////

function setup3D() {
    var scene, camera, renderer, diretionalLight, pen3D;

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(75,
        window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(0, 15, 20);
    renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    directionalLight = new THREE.DirectionalLight(0xffffaa, 0.7);
    directionalLight.position.set(-1, 2, 1);
    scene.add(directionalLight);
    scene.add(new THREE.AmbientLight(0x9999ff));

    (function render() {
        requestAnimationFrame(render);
        // renderer.setSize( window.innerWidth, window.innerHeight );
        scene.rotateY(10/60/60);
        renderer.render(scene, camera);
    })();

    window.addEventListener(
        'resize',
        function(){
            renderer.setSize( window.innerWidth, window.innerHeight );
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
       },
       false
    );

    pen3D = {
        drawTrunk: function(root, size) {
            // root.skin = skin(0.5, 0.3, 0.2);
            root.add(new THREE.Mesh(new THREE.CylinderGeometry(size/12, size/10, size, 16),
                root.skin).translateY(size/2));
            root.add(new THREE.Mesh(new THREE.SphereGeometry(size/12, 16),
                root.skin).translateY(size));
            return root;
        },

        drawLeaf: function(stem) {
            stem.skin.color.setRGB(0, 1, 0);
            stem.add(new THREE.Mesh(new THREE.CylinderGeometry(0, 0.02, 0.6),
                stem.skin) .rotateX(0.3).translateY(0.3));
            stem.add(new THREE.Mesh(new THREE.CircleGeometry(0.2),
                stem.skin) .rotateX(0.3).translateY(0.4));
            return stem;
        },

        Thing: function() {
            THREE.Object3D.call(this);
            this.skin = new THREE.MeshLambertMaterial({
                color: new THREE.Color(0.5, 0.3, 0.2),
                vertexColors: THREE.FaceColors,
                side: THREE.DoubleSide
            })
        }
    };

    pen3D.Thing.prototype = Object.create(THREE.Object3D.prototype);
    pen3D.Thing.prototype.tilt = pen3D.Thing.prototype.rotateX;
    pen3D.Thing.prototype.turn = pen3D.Thing.prototype.rotateY;
    pen3D.Thing.prototype.move = pen3D.Thing.prototype.translateY;

    pen3D.center = new pen3D.Thing;
    scene.add(pen3D.center);

    return pen3D;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>

Das 3D-Zeug ist in setup3D() versteckt und wird nur eingefügt, damit es weniger langweilig ist als console.log(). Winkel werden übrigens in Radiant gemessen.

Funktioniert in Firefox und Chrome. Nicht implementiert in Internet Explore und iOS (iPads). Versuchen Sie, es selbst auszuführen.

Nach einem weiteren Durchgang durch die Antworten habe ich festgestellt, dass Gabriel Ratener vor einem Jahr eine ähnliche Antwort gegeben hat zu Was ist die JavaScript-Version von sleep()? .

4voto

naugtur Punkte 16667

Zunächst einmal - setTimeout und setInterval sind das, was debe verwendet werden, da JavaScript ein Rückrufsystem ist. Wenn Sie Folgendes verwenden möchten sleep() es ist der Kontrollfluss oder die Architektur Ihres Codes, der falsch ist.

Dennoch kann ich bei der Umsetzung eines Schlafes in zwei Fällen helfen.

1. Aus dem Stegreif einen Synchronlauf vortäuschen:

// A module to do that //dual-license: MIT or WTF [you can use it anyhow and leave my nickname in a comment if you want to]
var _ = (function(){
  var queue = [];
  var play = function(){
    var go = queue.shift();
      if(go) {
        if(go.a) {
          go.f();
          play();
        }
        else
        {
          setTimeout(play, go.t);
        }
      }
  }
  return {
    go:function(f){
      queue.push({a:1, f:f});
    },
    sleep:function(t){
      queue.push({a:0, t:t});
    },
    playback:play
  }
})();

(Eine automatische Wiedergabe sollte ebenfalls möglich sein)

// Usage

_.go(function(){

  // Your code
  console.log('first');

});

_.sleep(5000);

_.go(function(){

  // Your code
  console.log('next');

});

// This triggers the simulation
_.playback();

2. Echter Synchronlauf

Ich habe eines Tages viel darüber nachgedacht, und die einzige Idee, die ich für einen echten Schlaf in JavaScript hatte, ist technischer Natur.

Eine Schlaffunktion müsste eine synchron Ajax-Aufruf mit einer Zeitüberschreitung, die auf den sleep-Wert eingestellt ist. Das ist alles und der einzige Weg, um eine echte sleep() .

4voto

CoR Punkte 3668

Code entnommen aus dieser Link wird der Computer nicht einfrieren. Aber es funktioniert nur in Firefox.

/**
 * Netscape compatible WaitForDelay function.
 * You can use it as an alternative to Thread.Sleep() in any major programming language
 * that support it while JavaScript it self doesn't have any built-in function to do such a thing.
 * parameters:
 * (Number) delay in millisecond
 */
function nsWaitForDelay(delay) {
    /**
     * Just uncomment this code if you're building an extension for Firefox.
     * Since Firefox 3, we'll have to ask for user permission to execute XPCOM objects.
     */
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");

    // Get the current thread.
    var thread = Components.classes["@mozilla.org/thread-manager;1"].getService(Components.interfaces.nsIThreadManager).currentThread;

    // Create an inner property to be used later as a notifier.
    this.delayed = true;

    /* Call JavaScript setTimeout function
      * to execute this.delayed = false
      * after it finishes.
      */
    setTimeout("this.delayed = false;", delay);

    /**
     * Keep looping until this.delayed = false
     */
    while (this.delayed) {
        /**
         * This code will not freeze your browser as it's documented in here:
         * https://developer.mozilla.org/en/Code_snippets/Threads#Waiting_for_a_background_task_to_complete
         */
        thread.processNextEvent(true);
    }
}

4voto

BishopZ Punkte 6049

Wenn Sie eine Sleep-Funktion wie diese schreiben

var sleep = function(period, decision, callback){
    var interval = setInterval(function(){
        if (decision()) {
            interval = clearInterval(interval);
            callback();
        }
    }, period);
}

und Sie haben eine asynchrone Funktion, die Sie mehrfach aufrufen können,

var xhr = function(url, callback){
    // Make an Ajax request
    // Call a callback when the request fulfils
}

Und Sie richten Ihr Projekt so ein:

var ready = false;

function xhr1(){
    xhr(url1, function(){ ready = true;});
}
function xhr2(){
    xhr(url2, function(){ ready = true; });
}
function xhr3(){
    xhr(url3, function(){ ready = true; });
}

Dann können Sie dies tun:

xhr1();
sleep(100, function(){ return done; }, xhr2);
sleep(100, function(){ return done; }, xhr3);
sleep(100, function(){ return done; }, function(){
    // Do more
});

Anstelle einer endlosen Einrückung der Rückrufe wie hier:

xhr(url1, function(){
    xhr2(url2, function(){
        xhr3(url3, function(){
            // Do more
        });
    });
});

3voto

HoldOffHunger Punkte 14903

Wenn Sie Code wollen, der in allen Browsern verwendbar ist, dann verwenden Sie setTimeout() y clearTimeout() . Wenn Sie so weit in den Antworten lesen, werden Sie wahrscheinlich feststellen, dass die akzeptierte Antwort die gesamte JavaScript-Kompilierung in Internet Explorer 11 und nach Anwendung dieser Lösung scheint es, dass Etwa 5 % der Nutzer verwenden noch diesen aktiv entwickelten Browser und benötigen Unterstützung.

Das hat fast alles kaputt gemacht. Es gibt bekannte Berichte über Pfeilfunktionen, die die Funktionalität des Internet Explorer 11 für die Software von Drupal , WordPress , Amazon AWS , IBM und es gibt sogar eine eigene Diskussion darüber auf Stack Overflow .

Sieh es dir einfach an...

Browser Compatibility Chart - Arrow Function Expressions

Browser Compatibility Chart - setTimeout

Utilisez setTimeout() y clearTimeout() und das wird bei allen Browsern funktionieren...

Funktionierende JSBin-Demo

var timeout;

function sleep(delay) {
    if(timeout) {
        clearTimeout(timeout);
    }
    timeout = setTimeout(function() {
        myFunction();
    }, delay);
}

console.log("sleep for 1 second");
sleep(1000);

function myFunction() {
    console.log("slept for 1 second!");
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X