簡體   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