简体   繁体   English

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

[英]Why there is no sleep functionality in javascript when there is setTimeout and setInterval?

Why there no such function in javascript that sets a timeout for its continuation, saves the necessary state (the scope object and the execution point), terminates the script and gives the control back to the browser? Why there no such function in javascript that sets a timeout for its continuation, saves the necessary state (the scope object and the execution point), terminates the script and gives the control back to the browser? After the timeout expires the browser would load back the execution context and continues the script, and we would have a real non browser blocking sleep functionality that would work even if the JS engine is single threaded.超时到期后,浏览器将加载回执行上下文并继续执行脚本,我们将拥有一个真正的非浏览器阻塞睡眠功能,即使 JS 引擎是单线程的也能正常工作。

Why there is still no such functionality in javascript?为什么javascript还是没有这个功能? Why do we have to still slice our code into functions and set the timeouts to the next step to achieve the sleep effect?为什么还要把代码切片成函数,设置超时时间到下一步才能达到休眠效果呢?

I think 'sleep'ing is something you do not want in your browser.我认为“睡眠”是您不希望在浏览器中出现的东西。

First of all it might be not clear what has to happen and how a browser should behave when you actually sleep.首先,可能不清楚必须发生什么以及当您实际睡眠时浏览器应该如何表现。

  • Is the complete Script runtime sleeping?完整的脚本运行时是否处于休眠状态? Normally it should because you only have one thread running your code.通常它应该是因为你只有一个线程运行你的代码。 So what happens if other events oocur during sleep?那么如果在睡眠期间发生其他事件会发生什么? they would block, and as soon execution continues all blocked events would fire.他们会阻塞,一旦执行继续,所有阻塞的事件都会触发。 That will cause an odd behaviour as you might imagine (for instance mouse click events which are fired some time, maybe seconds, after the actual click).这将导致您可能想象的奇怪行为(例如,在实际单击后一段时间,可能是几秒钟内触发的鼠标单击事件)。 Or these events had to be ignored, which will lead to a loss of information.或者必须忽略这些事件,这将导致信息丢失。

  • What will happen to your browser?你的浏览器会发生什么? Shall it wait for sleep if the user clicks a (eg close window) button?如果用户单击(例如关闭窗口)按钮,它会等待睡眠吗? I think not, but this might actually call javascript code again (unload) which will not be able to be called since program execution is sleeping.我认为不会,但这实际上可能会再次调用 javascript 代码(卸载),由于程序执行处于休眠状态,因此无法调用该代码。

On a second thought sleep is a sign of poor program design.再三考虑,睡眠是程序设计不佳的标志。 Actually a program/function/you name it has a certain task, which shall be completed as soon as possible.其实一个程序/功能/你的名字有一定的任务,应该尽快完成。 Sometimes you have to wait for a result (for instance you wait for an XHR to complete) and you want to continue program execution meanwhile.有时您必须等待结果(例如,您等待 XHR 完成)并且您希望同时继续执行程序。 In this case you can and should use asynchronous calls.在这种情况下,您可以而且应该使用异步调用。 This results in two advantages:这带来了两个好处:

  • The speed of all scripts is enhanced (no blocking of other scripts due to sleep)提高所有脚本的速度(不会因为睡眠而阻塞其他脚本)
  • The code is executed exactly when it should and not before or after a certain event (which might lead to other problems like deadlocks if two functions check for the same condition...)代码在它应该执行的时候准确地执行,而不是在某个事件之前或之后执行(如果两个函数检查相同的条件,这可能会导致其他问题,如死锁......)

... which leads to another problem: Imagine two or more pieces of code would call sleep. ...这导致了另一个问题:想象两段或更多段代码会调用睡眠。 They would hinder themselves if they try to sleep at the same, maybe unnecessarily.如果他们试图同时睡觉,他们会妨碍自己,也许是不必要的。 This would cause a lot of trouble when you like to debug, maybe you even have difficulties in ensuring which function sleeps first, because you might control this behavior somehow.当你想调试时,这会带来很多麻烦,甚至可能你很难确保哪个 function 先休眠,因为你可能会以某种方式控制这种行为。

Well I think that it is one of the good parts of Javascript, that sleep does not exist.嗯,我认为这是 Javascript 的优点之一,睡眠不存在。 However it might be interesting how multithreaded javascripts could perform in a browser;)然而,多线程 javascripts 如何在浏览器中执行可能会很有趣;)

javascript is desgined for single process single thread runtime, and browser also puts UI rendering in this thread, so if you sleep the thread, UI rendering such as gif animation and element's event will also be blocked, the browser will be in "not responding" state. javascript是为单进程单线程运行而设计的,浏览器也将UI渲染放在这个线程中,所以如果你sleep线程,那么像gif animation这样的UI渲染和元素的事件也会被阻塞,浏览器会处于“无响应”状态state。

Maybe a combination of setTimeout and yield would work for your needs?也许 setTimeout 和 yield 的组合可以满足您的需求?

What's the yield keyword in JavaScript? JavaScript 中的 yield 关键字是什么?

You could keep local function scope while letting the browser keep going about its work.您可以保留本地 function scope,同时让浏览器继续工作。

Of course that is only in Mozilla at the moment?当然,目前仅在 Mozilla 中?

It sounds like what you're looking for here is a way to write asynchronous code in a way that looks synchronous.听起来您在这里寻找的是一种以看起来同步的方式编写异步代码的方法。 Well, by using Promises and asynchronous functions in the new ECMAscript 7 standard (an upcoming version of JavaScript), you actually can do that:好吧,通过在新的 ECMAscript 7 标准(即将推出的 JavaScript 版本)中使用Promises异步函数,您实际上可以做到这一点:

// 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.");

Output: Output:

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

( Try in Babel ) 在 Babel 中尝试

As you may have noticed from the output, this doesn't work quite the same way that sleep does in most languages.正如您可能已经从 output 中注意到的那样,这与sleep在大多数语言中的工作方式不同。 Rather than block execution until the sleep time expires, our sleep function immediately returns a Promise object which resolves after the specified number of seconds.我们的睡眠 function 不会在睡眠时间到期之前阻止执行,而是立即返回Promise object,它会在指定的秒数后解决

Our helloAfter function is also declared as async , which causes it to behave similarly.我们的helloAfter function 也被声明为async ,这导致它的行为类似。 Rather than block until its body finishes executing, helloAfter returns a Promise immediately when it is called. helloAfter 在其主体完成执行之前不会阻塞,而是在调用它时立即返回helloAfter This is why "Script finished executing."这就是“脚本执行完毕”的原因。 gets printed before "Hello, world.".在“Hello, world.”之前打印。

Declaring helloAfter as async also allows the use of the await syntax inside of it. helloAfter声明为async还允许在其中使用await语法。 This is where things get interesting.这就是事情变得有趣的地方。 await sleep(seconds * 1000); causes the helloAfter function to wait for the Promise returned by sleep to be resolved before continuing.导致helloAfter function 等待sleep返回的 Promise 在继续之前得到解决。 This is effectively what you were looking for: a seemingly synchronous sleep within the context of the asynchronous helloAfter function.这实际上是您正在寻找的:在异步helloAfter function 的上下文中看似同步的睡眠。 Once the sleep resolves, helloAfter continues executing, printing "Hello, world."一旦休眠解决, helloAfter继续执行,打印“Hello, world”。 and then resolving its own Promise.然后解决它自己的 Promise。

For more information on async/await, check out the draft of the async functions standard for ES7.有关 async/await 的更多信息,请查看 ES7的异步函数标准草案

Because "sleep()" in JavaScript would make for a potentially horrible user experience, by freezing the web browser and make it unresponsive.因为 JavaScript 中的“sleep()”会导致潜在的可怕用户体验,冻结 web 浏览器并使其无响应。

What you want is a combination of yield and Deferreds (from jquery for example).您想要的是yieldDeferreds的组合(例如来自jquery )。

It's called sometimes pseudoThreads, Light Threading or Green Threads.它有时被称为伪线程、轻线程或绿线程。 And you can do exactly what you want with them in javascript > 1.7.在 javascript > 1.7 中,您可以完全按照您的意愿使用它们。 And here is how:方法如下:

You'll need first to include this code:您首先需要包含以下代码:

$$ = 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);};

And of course call jquery code to get the $ JQuery acces (for Deferreds).当然也可以调用 jquery 代码来获得$ JQuery 访问权限(用于延期)。

Then you'll be able to define once for all a Sleep function:然后您就可以为所有 Sleep function 定义一次:

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

And use it (along with other function that could take sometime):并使用它(与其他可能需要一些时间的 function 一起使用):

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

And there's a fully featured demo function:还有一个功能齐全的演示 function:

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.");
});

As you can see, syntax is a little bit different:如您所见,语法有点不同:

Remember:记住:

  • any function that should have these features should be wrapped in $$(myFunc)任何应具有这些功能的 function 都应包含在$$(myFunc)
  • $$ will catch any yielded value from your function and resume it only when the yielded value has finished to be calculted. $$将从您的 function 中捕获任何产生的值,并仅在完成计算产生的值时恢复它。 If it's not a defered, it'll work also.如果它不是延迟的,它也会起作用。
  • Use 'Return' to return a value.使用“返回”返回一个值。
  • This will work only with Javascript 1.7 (which is supported in newer firefox version)这仅适用于 Javascript 1.7(在较新的 firefox 版本中受支持)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM