繁体   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? 超时到期后,浏览器将加载回执行上下文并继续执行脚本,我们将拥有一个真正的非浏览器阻塞睡眠功能,即使 JS 引擎是单线程的也能正常工作。

为什么javascript还是没有这个功能? 为什么还要把代码切片成函数,设置超时时间到下一步才能达到休眠效果呢?

我认为“睡眠”是您不希望在浏览器中出现的东西。

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

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

  • 你的浏览器会发生什么? 如果用户单击(例如关闭窗口)按钮,它会等待睡眠吗? 我认为不会,但这实际上可能会再次调用 javascript 代码(卸载),由于程序执行处于休眠状态,因此无法调用该代码。

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

  • 提高所有脚本的速度(不会因为睡眠而阻塞其他脚本)
  • 代码在它应该执行的时候准确地执行,而不是在某个事件之前或之后执行(如果两个函数检查相同的条件,这可能会导致其他问题,如死锁......)

...这导致了另一个问题:想象两段或更多段代码会调用睡眠。 如果他们试图同时睡觉,他们会妨碍自己,也许是不必要的。 当你想调试时,这会带来很多麻烦,甚至可能你很难确保哪个 function 先休眠,因为你可能会以某种方式控制这种行为。

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

javascript是为单进程单线程运行而设计的,浏览器也将UI渲染放在这个线程中,所以如果你sleep线程,那么像gif animation这样的UI渲染和元素的事件也会被阻塞,浏览器会处于“无响应”状态state。

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

JavaScript 中的 yield 关键字是什么?

您可以保留本地 function scope,同时让浏览器继续工作。

当然,目前仅在 Mozilla 中?

听起来您在这里寻找的是一种以看起来同步的方式编写异步代码的方法。 好吧,通过在新的 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:

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

在 Babel 中尝试

正如您可能已经从 output 中注意到的那样,这与sleep在大多数语言中的工作方式不同。 我们的睡眠 function 不会在睡眠时间到期之前阻止执行,而是立即返回Promise object,它会在指定的秒数后解决

我们的helloAfter function 也被声明为async ,这导致它的行为类似。 helloAfter 在其主体完成执行之前不会阻塞,而是在调用它时立即返回helloAfter 这就是“脚本执行完毕”的原因。 在“Hello, world.”之前打印。

helloAfter声明为async还允许在其中使用await语法。 这就是事情变得有趣的地方。 await sleep(seconds * 1000); 导致helloAfter function 等待sleep返回的 Promise 在继续之前得到解决。 这实际上是您正在寻找的:在异步helloAfter function 的上下文中看似同步的睡眠。 一旦休眠解决, helloAfter继续执行,打印“Hello, world”。 然后解决它自己的 Promise。

有关 async/await 的更多信息,请查看 ES7的异步函数标准草案

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

您想要的是yieldDeferreds的组合(例如来自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 定义一次:

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

并使用它(与其他可能需要一些时间的 function 一起使用):

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

还有一个功能齐全的演示 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.");
});

如您所见,语法有点不同:

记住:

  • 任何应具有这些功能的 function 都应包含在$$(myFunc)
  • $$将从您的 function 中捕获任何产生的值,并仅在完成计算产生的值时恢复它。 如果它不是延迟的,它也会起作用。
  • 使用“返回”返回一个值。
  • 这仅适用于 Javascript 1.7(在较新的 firefox 版本中受支持)

暂无
暂无

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

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