Что такое JavaScript-версия sleep ()? - PullRequest
1903 голосов
/ 04 июня 2009

Есть ли лучший способ создать sleep в JavaScript, чем следующая pausecomp функция ( взято отсюда )?

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

Это не дубликат Сон в JavaScript - задержка между действиями ; Мне нужен реальный сон в середине функции, а не задержка перед выполнением фрагмента кода.

Ответы [ 73 ]

6 голосов
/ 14 января 2013

Это можно сделать с помощью метода сна Java. Я протестировал его в FF и IE, и он не блокирует компьютер, не проверяет ресурсы и не вызывает бесконечные попадания на сервер. Для меня это чистое решение.

Сначала вы должны загрузить Java на странице и сделать ее методы доступными. Для этого я сделал это:

<html>
<head>

<script type="text/javascript">

  function load() {
    var appletRef = document.getElementById("app");
    window.java = appletRef.Packages.java;
  } // endfunction

</script>

<body onLoad="load()">

<embed id="app" code="java.applet.Applet" type="application/x-java-applet" MAYSCRIPT="true" width="0" height="0" />

Тогда все, что вам нужно сделать, когда вы хотите безболезненную паузу в вашем JS, это:

java.lang.Thread.sleep(xxx)

Где ххх - время в миллисекундах. В моем случае (для обоснования) это было частью выполнения внутреннего заказа в очень маленькой компании, и мне нужно было распечатать счет, который должен был быть загружен с сервера. Я сделал это, загрузив счет (как веб-страницу) в iFrame, а затем распечатал iFrame. Конечно, мне пришлось ждать полной загрузки страницы, прежде чем я смог распечатать, поэтому JS пришлось сделать паузу. Я достиг этого, заставив страницу накладной (в iFrame) изменить скрытое поле формы на родительской странице с помощью события onLoad. И код на родительской странице для печати счета-фактуры выглядел следующим образом (несущественные части вырезаны для ясности):

var isReady = eval('document.batchForm.ready');
isReady.value=0;

frames['rpc_frame'].location.href=url;

while (isReady.value==0) {
  java.lang.Thread.sleep(250);
} // endwhile

window.frames['rpc_frame'].focus();
window.frames['rpc_frame'].print();

Таким образом, пользователь нажимает кнопку, скрипт загружает страницу счета, затем ждет, проверяя каждую четверть секунды, чтобы увидеть, закончилась ли загрузка страницы счета, а затем выскакивает диалоговое окно печати, чтобы пользователь мог отправить его на принтер. QED.

6 голосов
/ 20 декабря 2012

Добавление двух моих битов. Я нуждался в занятом ожидании для целей тестирования. Я не хотел разбивать код, так как это было бы много работы, поэтому простой for сделал это для меня.

for (var i=0;i<1000000;i++){                    
     //waiting
  }

Я не вижу никаких недостатков в этом, и это помогло мне.

6 голосов
/ 04 января 2015

Множество ответов не (прямо) отвечают на вопрос, и этот ответ не дает ...

Вот мои два цента (или функции):

Если вам нужно меньше неуклюжих функций, чем setTimeout и setInterval, вы можете заключить их в функции, которые просто меняют порядок аргументов и дают им хорошие имена:

function after(ms, fn){ setTimeout(fn, ms); }
function every(ms, fn){ setInterval(fn, ms); }

версии CoffeeScript:

after = (ms, fn)-> setTimeout fn, ms
every = (ms, fn)-> setInterval fn, ms

Затем вы можете красиво использовать их с анонимными функциями:

after(1000, function(){
    console.log("it's been a second");
    after(1000, function(){
        console.log("it's been another second");
    });
});

Теперь он легко читается как "после N миллисекунд, ..." (или "каждые N миллисекунд, ...")

6 голосов
/ 18 октября 2010

Один из сценариев, в котором вам может потребоваться функция sleep () вместо использования setTimeout (), - это если у вас есть функция, реагирующая на щелчок пользователя, который в конечном итоге в конечном итоге откроет новое, т.е. всплывающее окно, и вы инициировали некоторую обработку, которая требует короткий период до завершения отображения всплывающего окна. Перемещение открытого окна в закрытие означает, что оно обычно блокируется браузером.

5 голосов
/ 04 июня 2009

Вы не можете так спать в JavaScript, или, скорее, не должны. Запуск цикла sleep или while приведет к зависанию браузера пользователя до завершения цикла.

Используйте таймер, как указано в ссылке, на которую вы ссылались.

5 голосов
/ 27 мая 2010

Для конкретного случая, когда требуется разметить набор вызовов, выполняемых циклом, вы можете использовать что-то вроде приведенного ниже кода с прототипом. Без прототипа вы можете заменить функцию задержки на setTimeout.

function itemHandler(item)
{
    alert(item);
}

var itemSet = ['a','b','c'];

// Each call to itemHandler will execute
// 1 second apart
for(var i=0; i<itemSet.length; i++)
{
    var secondsUntilExecution = i;
    itemHandler.delay(secondsUntilExecution, item)
}
5 голосов
/ 10 октября 2015

Старый вопрос 2009 года. Теперь в 2015 году возможно новое решение с генераторами, определенными в ECMAscript 2015 aka ES6. Он был одобрен в июне, но ранее он был реализован в Firefox и Chrome Теперь функция сна может быть сделана не занятой, неблокируемой и вложенной в циклы и подфункции без зависания браузера. Требуется только чистый JavaScript, без библиотек и фреймворков.

В программе ниже показано, как можно сделать sleep() и runSleepyTask(). Функция sleep() является только оператором yield. Это так просто, что на самом деле проще написать оператор yield непосредственно вместо вызова sleep(), но тогда не будет сна-слова :-) yield возвращает значение времени методу next() внутри wakeup() и ждет. Фактический «сон» выполняется в wakeup() с использованием старого доброго setTimeout(). При обратном вызове метод next() вызывает выполнение оператора yield для продолжения, и «волшебство» yield заключается в том, что все локальные переменные и весь стек вызовов вокруг него все еще не повреждены.

Функции, которые используют sleep () или yield, должны быть определены как генераторы. Это легко сделать, добавив звездочку к ключевому слову function*. Выполнить генератор немного сложнее. При вызове с ключевым словом new генератор возвращает объект, имеющий метод next(), но тело генератора не выполняется (ключевое слово new является необязательным и не имеет значения). Метод next() запускает выполнение тела генератора, пока не встретит yield. Функция обёртки runSleepyTask() запускает пинг-понг: next() ожидает yield, а yield ожидает next().

Еще один способ вызвать генератор - с ключевым словом yield*, здесь он работает как простой вызов функции, но также включает возможность возврата к next().

Это все продемонстрировано на примере drawTree(). Он рисует дерево с листьями на вращающейся трехмерной сцене. Дерево нарисовано в виде ствола с 3-мя частями вверху в разных направлениях. Каждая часть затем рисуется как другое, но меньшее дерево, вызывая drawTree() рекурсивно после короткого сна. Очень маленькое дерево нарисовано только как лист.

У каждого листа своя жизнь в отдельной задаче, начинающейся с runSleepyTask(). Он рождается, растет, сидит, исчезает, падает и умирает в growLeaf(). Скорость контролируется с помощью sleep(). Это демонстрирует, как легко сделать многозадачность.

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>

3D-содержимое скрыто внутри setup3D () и включено только для того, чтобы сделать его менее скучным, чем console.log (). Кстати, ангелы измеряются в радианах.

Проверено на работу в Firefox и Chrome. Не реализовано в Internet Explore и iOS (iPad). Попробуйте запустить его самостоятельно.

После очередного прохода ответов я обнаружил, что Габриэль Ратенер сделал аналогичный ответ год назад: https://stackoverflow.com/a/24401317/5032384

5 голосов
/ 06 февраля 2015

Если вы используете node.js, вы можете взглянуть на fiber - собственное расширение C для узла, своего рода многопоточное моделирование.

Это позволяет вам делать реальные sleep способом, который блокирует выполнение в волокне, но не блокирует в основном потоке и других волокнах.

Вот свежий пример из собственного readme:

// sleep.js

var Fiber = require('fibers');

function sleep(ms) {
    var fiber = Fiber.current;
    setTimeout(function() {
        fiber.run();
    }, ms);
    Fiber.yield();
}

Fiber(function() {
    console.log('wait... ' + new Date);
    sleep(1000);
    console.log('ok... ' + new Date);
}).run();
console.log('back in main');

- и результаты:

$ node sleep.js
wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
back in main
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
4 голосов
/ 09 августа 2011

Прежде всего - setTimeout и setInterval - это то, что следует использовать , из-за природы обратного вызова javascript. Если вы хотите использовать sleep(), это неверный поток управления или архитектура вашего кода.

Сказав, что, я полагаю, я все еще могу помочь с двумя реализациями сна.

  1. фальшивый синхронный бег с макушки моей головы:

    //a module to do taht //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 
     }
    })();
    

    [автоматическое воспроизведение также должно быть возможно]

    //usage
    
    _.go(function(){
    
    //your code
    console.log('first');
    
    });
    
    
    _.sleep(5000);
    
    _.go(function(){
    
    //your code
    console.log('next');
    
    });
    
    //this triggers the simulation
    _.playback();
    
  2. реальный синхронный прогон

Однажды я много думал об этом, и единственная идея, которая у меня была для настоящего сна в javascript, - техническая.

функция сна должна быть синхронным AJAX-вызовом с таймаутом, установленным в значение сна. Вот и все, и единственный способ получить настоящий sleep()

4 голосов
/ 01 июля 2018

Начиная с узла 7.6 , вы можете объединить функцию promisify из модуля утилит с setTimeout.

const sleep = require('util').promisify(setTimeout)

Общее использование

async function main() {
    console.time("Slept for")
    await sleep(3000)
    console.timeEnd("Slept for")
}

main()

Использование вопроса

async function asyncGenerator() {
    while (goOn) {
      var fileList = await listFiles(nextPageToken);
      await sleep(3000)
      var parents = await requestParents(fileList);
    }
  }
...