Почему нет функции сна в javascript, когда есть setTimeout и setInterval? - PullRequest
5 голосов
/ 29 апреля 2011

Почему в javascript нет такой функции, которая устанавливает таймаут для его продолжения, сохраняет необходимое состояние (объект области действия и точку выполнения), завершает сценарий и возвращает управление обратно в браузер? После истечения времени ожидания браузер загрузит контекст выполнения и продолжит выполнение сценария, и у нас будет настоящая не-браузерная блокирующая функция сна, которая будет работать, даже если механизм JS является однопоточным.

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

Ответы [ 6 ]

5 голосов
/ 29 апреля 2011

Я думаю, что спать не стоит в браузере.

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

  • Спит ли полное время выполнения скрипта? Обычно так и должно быть, потому что у вас есть только один поток, выполняющий ваш код. Так что же произойдет, если во время сна возникнут другие события? они будут блокированы, и как только выполнение продолжится, все заблокированные события сработают. Это может вызвать странное поведение, как вы можете себе представить (например, события щелчка мыши, которые запускаются через некоторое время, может быть, секунды после фактического щелчка). Или эти события должны были быть проигнорированы, что приведет к потере информации.

  • Что будет с вашим браузером? Должен ли он ждать сна, если пользователь нажимает кнопку (например, закрыть окно)? Я думаю, что нет, но на самом деле это может снова вызвать код javascript (unload), который не сможет быть вызван, поскольку выполнение программы спит.

Если подумать, сон - это признак плохого дизайна программы. На самом деле программа / функция / вы называете это определенной задачей, которая должна быть выполнена как можно скорее. Иногда вам приходится ждать результата (например, вы ожидаете завершения XHR) и хотите продолжить выполнение программы. В этом случае вы можете и должны использовать асинхронные вызовы. Это дает два преимущества:

  • Увеличена скорость всех скриптов (нет блокировки других скриптов из-за сна)
  • Код выполняется именно тогда, когда он должен, а не до или после определенного события (что может привести к другим проблемам, таким как взаимоблокировки, если две функции проверяют одно и то же условие ...)

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

Ну, я думаю, что это одна из хороших частей Javascript, что сон не существует. Однако может быть интересно, как многопоточные javascript-скрипты могут работать в браузере;)

4 голосов
/ 29 апреля 2011

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

2 голосов
/ 29 апреля 2011

Может быть, комбинация setTimeout и yield подойдет для ваших нужд?

Какое ключевое слово yield в JavaScript?

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

Конечно, сейчас это только в Mozilla?

1 голос
/ 12 апреля 2016

Звучит так, будто вы ищете здесь способ написания асинхронного кода таким образом, чтобы выглядел синхронно. Ну, используя Обещания и асинхронные функции в новом стандарте ECMAscript 7 (будущая версия JavaScript), вы на самом деле можете сделать это:

// First we define our "sleep" function...
function sleep(milliseconds) {
  // Immediately return a promise that resolves after the
  // specified number of milliseconds.
  return new Promise(function(resolve, _) {
    setTimeout(resolve, milliseconds);
  });
}

// Now, we can use sleep inside functions declared as asynchronous
// in a way that looks like a synchronous sleep.
async function helloAfter(seconds) {
  console.log("Sleeping " + seconds + " seconds.");
  await sleep(seconds * 1000); // Note the use of await
  console.log("Hello, world!");
}

helloAfter(1);
console.log("Script finished executing.");

Выход:

Sleeping 1 seconds.
Script finished executing.
Hello, world!

( Попробуйте в Вавилоне )

Как вы, возможно, заметили из вывода, это работает не совсем так, как sleep в большинстве языков. Вместо выполнения блока до истечения времени ожидания наша функция ожидания немедленно возвращает объект Promise , который разрешает через указанное количество секунд.

Наша функция helloAfter также объявлена ​​как async, что приводит к ее аналогичному поведению. Вместо того, чтобы блокировать, пока не завершится выполнение его тела, helloAfter возвращает Обещание сразу же после его вызова. Вот почему «Сценарий завершился». печатается перед «Привет, мир!».

Объявление helloAfter как async также позволяет использовать внутри него синтаксис await. Здесь вещи становятся интересными. await sleep(seconds * 1000); заставляет функцию helloAfter ждать разрешения Promise, возвращаемого sleep, прежде чем продолжить. Это именно то, что вы искали: казалось бы, синхронный сон в контексте асинхронной функции helloAfter. Как только сон разрешается, helloAfter продолжает выполняться, печатая «Привет, мир!» и затем решить свое обещание.

Для получения дополнительной информации об асинхронности / ожидании посмотрите черновик стандарта асинхронных функций для ES7.

1 голос
/ 29 октября 2011

То, что вы хотите, это комбинация yield и Deferreds (например, jquery).

Это иногда называют псевдо-нитью, легкой нитью или зеленой нитью. И вы можете делать именно то, что вы хотите с ними в JavaScript> 1,7. А вот как:

Сначала вам нужно будет включить этот код:

$$ = function (generator) {
    var d = $.Deferred();
    var iter;
    var recall = function() {
       try {var def = iter.send.apply(iter, arguments);} catch(e) {
          if (e instanceof StopIteration) {d.resolve(); return;}
          if (e instanceof ReturnValueException) {
              d.resolve(e.retval); return
          };
          throw e;
       };
       $.when(def).then(recall);      // close the loop !
    };
    return function(arguments) {
         iter = generator.apply(generator, arguments);
         var def = iter.next();        // init iterator
         $.when(def).then(recall);     // loop in all yields
         return d.promise();           // return a deferred
    }
}

ReturnValueException = function (r) {this.retval = r; return this; };
Return = function (retval) {throw new ReturnValueException(retval);};

И, конечно, вызовите код jquery, чтобы получить $ доступ JQuery (для отложенных).

Тогда вы сможете один раз определить функцию Sleep:

function Sleep(time) {
  var def = $.Deferred();
  setTimeout(function() {def.resolve();}, time);
  return def.promise();
}

И используйте его (вместе с другой функцией, которая может занять некоторое время):

// Sample function that take 3 seconds to execute
fakeAjaxCall = $$(function () {
   yield (Sleep(3000));
   Return("AJAX OK");
});

А еще есть полнофункциональная демонстрационная функция:

function log(msg) {$('<div>'+msg+'</div>').appendTo($("#log")); }

demoFunction = $$(function (arg1, arg2) {
   var args = [].splice.call(arguments,0);
   log("Launched, arguments: " + args.join(", "));
   log("before sleep for 3secs...");
   yield (Sleep(3000));
   log("after sleep for 3secs.");

   log("before call of fake AjaxCall...");
   ajaxAnswer = yield (fakeAjaxCall());
   log("after call of fake AjaxCall, answer:" + ajaxAnswer);

   // You cannot use return, You'll have to use this special return
   // function to return a value
   log("returning 'OK'.");
   Return("OK");
   log("should not see this.");
});

Как видите, синтаксис немного отличается:

Помните:

  • любая функция, которая должна иметь эти функции, должна быть заключена в $$(myFunc)
  • $$ поймает любое полученное значение из вашей функции и возобновит его только тогда, когда Приведенная стоимость закончилась для расчета. Если это не побежденный, это будет работать также.
  • Используйте «Return» для возврата значения.
  • Это будет работать только с Javascript 1.7 (который поддерживается в более новой версии Firefox)
1 голос
/ 29 апреля 2011

Потому что «sleep ()» в JavaScript создаст потенциально ужасный пользовательский интерфейс, заморозив веб-браузер и сделав его безразличным

...