Что такое JavaScript-версия sleep ()?

Вы можете использовать общий (общий) метод для чтения атрибута над данным MemberInfo

public static bool TryGetAttribute<T>(MemberInfo memberInfo, out T customAttribute) where T: Attribute {
                var attributes = memberInfo.GetCustomAttributes(typeof(T), false).FirstOrDefault();
                if (attributes == null) {
                    customAttribute = null;
                    return false;
                }
                customAttribute = (T)attributes;
                return true;
            }
1946
задан 12 revs, 10 users 38% 17 March 2018 в 18:15
поделиться

70 ответов

Большинство ответов здесь ошибочны или, по крайней мере, устарели. Нет причин, по которым javascript должен быть однопоточным, и на самом деле это не так. Сегодня все основные браузеры поддерживают рабочих, до этого другие среды исполнения javascript, такие как Rhino и Node.js, поддерживали многопоточность.

«Javascript является однопоточным» не является правильным ответом. Например, выполнение функции сна внутри рабочего не блокирует какой-либо код, выполняемый в потоке пользовательского интерфейса.

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

// This is based on the latest ES6 drafts.
// js 1.7+ (SpiderMonkey/Firefox 2+) syntax is slightly different

// run code you want to sleep here (ommit star if using js 1.7)
function* main(){
    for (var i = 0; i < 10; i++) {
        // to sleep for 10 milliseconds 10 times in a row
        yield 10;
    }

    yield 5;
    console.log('I just slept 5 milliseconds!');
}

// resume the given generator after ms milliseconds
function resume(ms, generator){
    setTimeout(function(){
        // ommit .value if using js 1.7
        var nextSleep = generator.next().value;
        resume(nextSleep, generator);
    }, ms);
}

// initialize generator and get first sleep for recursive function
var
    generator = main(),
    firstSleep = generator.next().value;

// initialize recursive resume function
resume(firstSleep, generator);

Эта имитация сна отличается от истинной функции сна, поскольку она не заблокировать поток. Это просто сахар поверх текущей функции javascript setTimeout. Этот тип функциональности был реализован в Task.js и должен работать сегодня в Firefox.

9
ответ дан 3 revs, 2 users 90% 17 March 2018 в 18:15
поделиться

Хорошей альтернативой в некоторых ситуациях является отображение панели сообщений верхнего уровня, чтобы остановить взаимодействие с пользователем, а затем скрыть ее снова, когда вы получите ожидаемый результат (асинхронно). Это позволяет браузеру выполнять фоновые задачи, но приостанавливает рабочий процесс до тех пор, пока вы не вернете свой результат.

0
ответ дан Matthew Strawbridge 17 March 2018 в 18:15
поделиться

Метод объекта, который должен использовать метод «сна», такой как следующий:

function SomeObject() {
    this.SomeProperty = "xxx";
    return this;
}
SomeObject.prototype.SomeMethod = function () {
    this.DoSomething1(arg1);
    sleep(500);
    this.DoSomething2(arg1);
}

Может быть почти переведен на:

function SomeObject() {
    this.SomeProperty = "xxx";
    return this;
}
SomeObject.prototype.SomeMethod = function (arg1) {
    var self = this;
    self.DoSomething1(arg1);
    setTimeout(function () {
        self.DoSomething2(arg1);
    }, 500);
}

Разница в том, что операция SomeMethod возвращается до выполнения операции DoSomething2. Вызывающий "SomeMethod" не может зависеть от этого. Поскольку метод «Sleep» не существует, я использую более поздний метод и соответствующим образом проектирую свой код.

Надеюсь, это поможет.

1
ответ дан Michael Erickson 17 March 2018 в 18:15
поделиться

Я перебираю решение в течение дня, но все еще думаю, как сохранить цепочку при использовании обратного вызова. Все знакомы с традиционным стилем программирования, в котором код выполняется построчно, синхронно. SetTimeout использует обратный вызов, поэтому следующая строка не ожидает его завершения. Это позволило мне подумать, как сделать его "синхронизированным", чтобы сделать функцию "сна".

Начиная с простой сопрограммы:

function coroutine() {
    console.log('coroutine-1:start');
    sleepFor(3000); //sleep for 3 seconds here
    console.log('coroutine-2:complete');
}

Я хочу поспать 3 секунды посередине, но не хочу доминировать над всем потоком, поэтому сопрограмма должна выполняться другим потоком. Я рассматриваю Unity YieldInstruction и изменяю сопрограмму следующим образом:

function coroutine1() {
    this.a = 100;
    console.log('coroutine1-1:start');
    return sleepFor(3000).yield; // sleep for 3 seconds here
    console.log('coroutine1-2:complete');
    this.a++;
}

var c1 = new coroutine1();

Объявите прототип sleepFor:

sleepFor = function(ms) {
    var caller = arguments.callee.caller.toString();
    var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
    var args = arguments.callee.caller.arguments;
    var funcBody = caller.replace(/^[\s\S]*?sleepFor[\s\S]*?yield;|}[\s;]*$/g,'');
    var context = this;
    setTimeout(function() {
        new Function(funcArgs, funcBody).apply(context, args);
    }, ms);
    return this;
}

После запуска сопрограммы1 (я проверил в IE11 и Chrome49) вы увидите, что он спит 3 секунды между двумя операторами консоли. Он сохраняет коды так же красиво, как и традиционный стиль. Хитрость во сне для рутины. Он читает тело функции вызывающей стороны как строку и разбивает его на 2 части. Снимите верхнюю часть и создайте другую функцию нижней частью. После ожидания указанного количества миллисекунд он вызывает созданную функцию, применяя исходный контекст и аргументы. Для оригинального потока это закончится "возвращением" как обычно. За "доходность"? Он используется для сопоставления регулярных выражений. Это необходимо, но бесполезно.

1116 Это не на все 100% идеально, но, по крайней мере, оно достигает моей работы. Я должен упомянуть некоторые ограничения в использовании этой части кода. Поскольку код разбивается на 2 части, оператор return должен быть во внешнем, а не в любом цикле или {}. т.е.

function coroutine3() {
    this.a = 100;
    console.log('coroutine3-1:start');
    if(true) {
        return sleepFor(3000).yield;
    } // <- raise exception here
    console.log('coroutine3-2:complete');
    this.a++;
}

Приведенные выше коды должны иметь проблему, поскольку закрывающая скобка не может существовать отдельно в созданной функции. Другое ограничение - все локальные переменные, объявленные как "var xxx = 123", не могут быть перенесены в следующую функцию. Вы должны использовать «this.xxx = 123» для достижения того же. Если у вашей функции есть аргументы, и они получили изменения, измененное значение также не может быть перенесено в следующую функцию.

function coroutine4(x) { // assume x=abc
    var z = x;
    x = 'def';
    console.log('coroutine4-1:start' + z + x); //z=abc, x=def
    return sleepFor(3000).yield;
    console.log('coroutine4-2:' + z + x); //z=undefined, x=abc
}

Я бы представил еще один прототип функции: waitFor

waitFor = function(check, ms) {
    var caller = arguments.callee.caller.toString();
    var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
    var args = arguments.callee.caller.arguments;
    var funcBody = caller.replace(/^[\s\S]*?waitFor[\s\S]*?yield;|}[\s;]*$/g,'');
    var context = this;
    var thread = setInterval(function() {
        if(check()) {
            clearInterval(thread);
            new Function(funcArgs, funcBody).apply(context, args);
        }
    }, ms?ms:100);
    return this;
}

Он ожидает функцию «check», пока не вернет true. Он проверяет значение каждые 100 мс. Вы можете настроить его, передав дополнительный аргумент. Рассмотрим тестирование coroutine2:

function coroutine2(c) {
    /* some codes here */
    this.a = 1;
    console.log('coroutine2-1:' + this.a++);
    return sleepFor(500).yield;

    /* next */
    console.log('coroutine2-2:' + this.a++);
    console.log('coroutine2-2:waitFor c.a>100:' + c.a);
    return waitFor(function() {
        return c.a>100;
    }).yield;

    /* the rest of code */
    console.log('coroutine2-3:' + this.a++);
}

Также в красивом стиле, который мы любим до сих пор. На самом деле я ненавижу вложенный обратный вызов. Легко понять, что сопрограмма2 будет ждать завершения сопрограммы1. Интересно? Хорошо, затем запустите следующие коды:

this.a = 10;
console.log('outer-1:' + this.a++);
var c1 = new coroutine1();
var c2 = new coroutine2(c1);
console.log('outer-2:' + this.a++);

Вывод:

outer-1:10
coroutine1-1:start
coroutine2-1:1
outer-2:11
coroutine2-2:2
coroutine2-2:waitFor c.a>100:100
coroutine1-2:complete
coroutine2-3:3

Наружное завершение сразу же после инициализированных сопрограмм1 и сопрограмм2. Затем coroutine1 будет ждать 3000 мс. Coroutine2 войдет в шаг 2 после ожидания 500 мс. После этого он продолжит шаг 3, как только обнаружит значения coroutine1.a> 100.

Остерегайтесь того, что есть 3 контекста для хранения переменной «a». Один является внешним, значения которого равны 10 и 11. Другой находится в coroutine1, значения которого равны 100 и 101. Последний находится в coroutine2, значения которого равны 1,2 и 3. В coroutine2 он также ожидает ca, который приходит от coroutine1, пока его значение не станет больше 100. 3 контекста являются независимыми.

Весь код для копирования и вставки:

sleepFor = function(ms) {
    var caller = arguments.callee.caller.toString();
    var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
    var args = arguments.callee.caller.arguments;
    var funcBody = caller.replace(/^[\s\S]*?sleepFor[\s\S]*?yield;|}[\s;]*$/g,'');
    var context = this;
    setTimeout(function() {
        new Function(funcArgs, funcBody).apply(context, args);
    }, ms);
    return this;
}

waitFor = function(check, ms) {
    var caller = arguments.callee.caller.toString();
    var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
    var args = arguments.callee.caller.arguments;
    var funcBody = caller.replace(/^[\s\S]*?waitFor[\s\S]*?yield;|}[\s;]*$/g,'');
    var context = this;
    var thread = setInterval(function() {
        if(check()) {
            clearInterval(thread);
            new Function(funcArgs, funcBody).apply(context, args);
        }
    }, ms?ms:100);
    return this;
}

function coroutine1() {
    this.a = 100;
    console.log('coroutine1-1:start');
    return sleepFor(3000).yield;
    console.log('coroutine1-2:complete');
    this.a++;
}

function coroutine2(c) {
    /* some codes here */
    this.a = 1;
    console.log('coroutine2-1:' + this.a++);
    return sleepFor(500).yield;

    /* next */
    console.log('coroutine2-2:' + this.a++);
    console.log('coroutine2-2:waitFor c.a>100:' + c.a);
    return waitFor(function() {
        return c.a>100;
    }).yield;

    /* the rest of code */
    console.log('coroutine2-3:' + this.a++);
}

this.a = 10;
console.log('outer-1:' + this.a++);
var c1 = new coroutine1();
var c2 = new coroutine2(c1);
console.log('outer-2:' + this.a++);

Он протестирован в IE11 и Chrome49. Поскольку он использует arguments.callee, это может вызвать проблемы, если он работает в строгом режиме.

0
ответ дан 3 revs 17 March 2018 в 18:15
поделиться

У меня был этот вопрос в течение долгого времени, и мне нужен был не тот ответ, который был здесь представлен. Эта функция ожидания вызывает синхронное ожидание, которое не связывает процессор. waitForIt отправляет ajax-запрос в любое место и устанавливает для флага асинхронности значение false. waitF делает то же самое с фреймом, а waitD делает то же самое с div. Ajax занимает около 100 мс, фрейм - около 25, а div - около 1. Функция ожидания использует все это в зависимости от того, сколько времени вы даете. Если это не заставило себя долго ждать, сделайте это снова. Мне это нужно при работе с несколькими асинхронными загрузочными элементами. В основном для «подождите, пока этот элемент существует». Вы можете поиграть с ним здесь https://jsfiddle.net/h2vm29ue/ Он просто использует то, что естественно ожидает браузер. Более длинная версия https://jsfiddle.net/5cov1p0z/32/ является более точной.

 function waitForIt() {
     var start = new Date();
     var xhttp = new XMLHttpRequest();
     xhttp.onreadystatechange = function() {
         if (this.readyState == 4 && this.status == 200) {
            //doesn't matter
         }
     };
     xhttp.open("GET", "WaitForIt", false);
     xhttp.send();
     var end = new Date();
 }
 //



 function waitF() {
     var start = new Date();
     var ifram = document.createElement('iframe');
     ifram.id = 'ifram';
     ifram.src = '';
     var div = document.createElement('div');
     div.id = 'timer';
     document.body.appendChild(div);
     document.getElementById('timer').appendChild(ifram);
     document.getElementById('timer').removeChild(ifram);
     document.body.removeChild(div);
     var end = new Date();
     return (end - start);
 }


 function waitD() {
     var start = new Date();
     var div = document.createElement('div');
     div.id = 'timer';
     document.body.appendChild(div);
     div.click();
     document.body.removeChild(div);
     var end = new Date();
     return (end - start);
 }

 function wait(time) {
     var start = new Date();
     var end = new Date();
     while ((end - start < time)) {

         if ((time - (end - start)) >= 200) {
             waitForIt();
         } else {
             if ((time - (end - start)) >= 50) {
                 waitF();
             } else {
                 waitD();
             }

         }
         end = new Date();
     }
     return (end - start);
 }
0
ответ дан 3 revs 17 March 2018 в 18:15
поделиться

Старый вопрос 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
ответ дан 5 revs 17 March 2018 в 18:15
поделиться

Обновление 2019 года с использованием Atomics.wait

Должно работать в узле 9.3 или выше.

Мне нужен был довольно точный таймер в Node.js, и он прекрасно работает для этого. Однако кажется, что поддержка браузеров крайне ограничена.

let ms = 10000;
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);

Провел несколько 10-секундных тестов таймера.

С setTimeout я получаю ошибку до 7000 микросекунд. (7 мс)

При использовании Atomics моя ошибка, по-видимому, не превышает 600 мкс. (0.6ms)

1
ответ дан Kapytanhook 17 March 2018 в 18:15
поделиться

Это может сработать. Это работало для меня в c и javascript.

function sleep(time) {
 var x = 0;
 for(x = 0;x < time;x++) {/* Do nothing*/}
}
-4
ответ дан Spaceboy Ross 17 March 2018 в 18:15
поделиться

В JavaScript я переписываю каждую функцию, чтобы она могла завершиться как можно скорее. Вы хотите, чтобы браузер снова контролировал ситуацию, чтобы он мог вносить изменения в вашу модель DOM.

Каждый раз, когда я хотел спать в середине моей функции, я реорганизовывал использование setTimeout () .

] Я собираюсь отредактировать этот ответ, потому что считаю его полезным:

Печально известная функция сна, или задержка, в любом языке широко обсуждается. Некоторые скажут, что всегда должен быть сигнал или обратный вызов для запуска данной функции, другие будут утверждать, что иногда полезен произвольный момент задержки. Я говорю, что каждому свое, и одно правило никогда ничего не может диктовать в этой отрасли.

Написать функцию сна просто и сделать ее еще более удобной с помощью JavaScript Promises:

// sleep time expects milliseconds
function sleep (time) {
  return new Promise((resolve) => setTimeout(resolve, time));
}

// Usage!
sleep(500).then(() => {
    // Do something after the sleep!
});
633
ответ дан 22 November 2019 в 19:59
поделиться

Самое короткое решение без любых зависимостей:

await new Promise(resolve => setTimeout(resolve, 5000));
2
ответ дан 22 November 2019 в 19:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: