为什么有setTimeout和setInterval时javascript中没有睡眠功能?

时间:2011-04-29 12:51:07

标签: javascript browser language-design

为什么javascript中没有这样的函数为其继续设置超时,保存必要的状态(范围对象和执行点),终止脚本并将控件返回给浏览器?超时到期后,浏览器将加载执行上下文并继续执行脚本,即使JS引擎是单线程的,我们也会有一个真正的非浏览器阻塞睡眠功能。

为什么javascript中仍然没有这样的功能?为什么我们仍然需要将代码切换成函数并将超时设置为下一步以实现睡眠效果?

6 个答案:

答案 0 :(得分:5)

我认为'睡眠'是你不想在浏览器中使用的东西。

首先,可能不清楚当您实际入睡时应该发生什么以及浏览器应该如何表现。

  • 完整的脚本运行时是否正在睡眠?通常它应该是因为你只有一个线程运行你的代码。那么,如果在睡眠期间发生其他事件,会发生什么?他们会阻止,并且一旦执行继续,所有被阻止的事件都将被触发。这将导致您可能想象的奇怪行为(例如,鼠标点击事件在实际点击后的某个时间,可能是秒)。或者必须忽略这些事件,这将导致信息丢失。

  • 您的浏览器会发生什么?如果用户点击(例如关闭窗口)按钮,它应该等待睡眠吗?我想不是,但这实际上可能会再次调用javascript代码(卸载),因为程序执行正在休眠,所以无法调用它。

暂时认为睡眠是程序设计不佳的标志。实际上一个程序/功能/你命名它有一定的任务,应尽快完成。有时您必须等待结果(例如,您等待XHR完成)并且您希望同时继续执行程序。在这种情况下,您可以并且应该使用异步调用。这有两个好处:

  • 所有脚本的速度都得到了提升(不会因为睡眠而阻止其他脚本)
  • 代码执行时应该在某个事件之前或之后执行(如果两个函数检查相同的条件,可能会导致其他问题,如死锁...)

...导致另一个问题:想象一下,两段或多段代码会调用sleep。如果他们试图在同一时间睡觉,他们会阻碍自己,也许是不必要的。如果您想调试,这会造成很多麻烦,也许您甚至难以确保哪个函数首先处于休眠状态,因为您可能会以某种方式控制此行为。

我认为这是Javascript的优点之一,睡眠不存在。但是,多线程javascripts如何在浏览器中执行可能会很有趣;)

答案 1 :(得分:4)

javascript是为单进程单线程运行时设计的,浏览器也将UI呈现放在这个线程中,所以如果你睡眠线程,gif动画和元素事件等UI渲染也会被阻止,浏览器将在“没有回应“状态。

答案 2 :(得分:2)

也许setTimeout和yield的组合可以满足您的需求?

What's the yield keyword in JavaScript?

您可以保留本地功能范围,同时让浏览器继续开展工作。

当然那只是在Mozilla目前?

答案 3 :(得分:1)

因为JavaScript中的“sleep()”会导致潜在的可怕用户体验,因此冻结Web浏览器并使其无响应。

答案 4 :(得分:1)

您想要的是yieldDeferreds的组合(例如来自jquery)。

它有时被称为pseudoThreads,Light Threading或Green Threads。你可以在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访问(对于Deferreds)。

然后,您将能够为所有睡眠功能定义一次:

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)
  • $$将从您的函数中捕获任何已产生的值,并仅在以下时恢复它 已结算的值已完成计算。如果它不是一个被推迟的,它将起作用 也。
  • 使用“返回”返回值。
  • 这仅适用于Javascript 1.7(较新的firefox版本支持)

答案 5 :(得分:1)

听起来你在这里寻找的是一种以看起来同步的方式编写异步代码的方法。好吧,通过在新的ECMAscript 7标准(即将推出的JavaScript版本)中使用Promisesasynchronous functions,您实际上可以这样做:

// 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!

Try in Babel

正如您可能从输出中注意到的那样,这与sleep在大多数语言中的工作方式完全相同。我们的睡眠功能不会阻止执行直到睡眠时间到期,而是会在指定的秒数后立即返回解析Promise对象。

我们的helloAfter函数也被声明为async,这会导致它的行为类似。 helloAfter在被调用时立即返回Promise,而不是阻塞直到它的主体完成执行。这就是&#34;脚本完成执行的原因。&#34;在&#34; Hello,world!&#34;。

之前打印

helloAfter声明为async也允许在其中使用await语法。这是事情变得有趣的地方。 await sleep(seconds * 1000);导致helloAfter函数等待sleep返回的Promise在继续之前被解析。这实际上是您所寻找的:在异步helloAfter函数的上下文中看似同步的睡眠。一旦睡眠结算,helloAfter继续执行,打印&#34;你好,世界!&#34;然后解决自己的承诺。

有关async / await的更多信息,请查看ES7的draft of the async functions standard