簡體   English   中英

代碼執行中的同步延遲

[英]Synchronous delay in code execution

我有一個代碼需要在延遲 5000 毫秒后執行。目前我正在使用 setTimeout 但它是異步的,我希望執行等待它返回。 我嘗試使用以下內容:

function pauseComp(ms) 
 {
     var curr = new Date().getTime();
     ms += curr;
     while (curr   < ms) {
         curr = new Date().getTime();
     }
 } 

但是我想延遲的代碼是使用 raphaeljs 繪制一些對象並且顯示一點也不流暢。 我正在嘗試使用 doTimeout 插件。 我只需要延遲一次,因為延遲和要延遲的代碼都在一個循環中。 我不需要 ID,所以我沒有使用它。 例如:

for(i; i<5; i++){ $.doTimeout(5000,function(){
         alert('hi');  return false;}, true);}

這等待 5 秒,然后給出第一個 Hi,然后連續的循環迭代在第一個之后立即顯示警報。 我想讓它做的是等待 5 秒再次發出警報等待然后發出警報等等。

任何提示/建議表示贊賞!

接受的答案的變化與這個一樣好。

此外,我同意優先使用setTimeout和異步函數調用的警告,但有時,例如,在構建測試時,您只需要一個同步等待命令......

function wait(ms) {
    var start = Date.now(),
        now = start;
    while (now - start < ms) {
      now = Date.now();
    }
}

如果你想在幾秒鍾內得到它,在 while 檢查中將 start ms 除以 1000...

如果您想利用新的 async/await 語法,您可以將 set timeout 轉換為 Promise,然后等待它。

function wait(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("Done waiting");
      resolve(ms)
    }, ms )
  })
}  

(async function Main() {
  console.log("Starting...")
  await wait(5000);
  console.log("Ended!")
})();

同步等待(僅用於測試!):

const syncWait = ms => {
    const end = Date.now() + ms
    while (Date.now() < end) continue
}

用法:

console.log('one')
syncWait(5000)
console.log('two')

異步等待:

const asyncWait = ms => new Promise(resolve => setTimeout(resolve, ms))

用法:

(async () => {
    console.log('one')
    await asyncWait(5000)
    console.log('two')
})()

替代(異步):

const delayedCall = (array, ms) =>
    array.forEach((func, index) => setTimeout(func, index * ms))

用法:

delayedCall([
    () => console.log('one'),
    () => console.log('two'),
    () => console.log('three'),
], 5000)

使用新的 Atomics API,您可以啟動同步延遲而不會出現性能峰值:

const sleep = milliseconds => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds)

sleep(5000) // Sleep for 5 seconds

console.log("Executed after 5 seconds!")

JavaScript 是一種單線程語言。 您不能將setTimeout和同步處理結合起來。 會發生的情況是,計時器會超時,但是 JS 引擎會等待處理結果,直到當前腳本完成。

如果要同步方法,直接調用方法即可!

如果您想在 setTimeout 之后處理某些內容,請包含它或從 timeout 函數中調用它。

非超時循環(檢查時間或計數到 1000000 或其他)只是鎖定瀏覽器。 setTimeout (或$.doTimeout插件)是最好的方法。

正如您所發現的那樣,在循環中創建超時將不起作用,因為循環不會在繼續之前等待先前的超時發生。 嘗試更多類似的東西:

// Generic function to execute a callback a given number
// of times with a given delay between each execution
function timeoutLoop(fn, reps, delay) {
  if (reps > 0)
    setTimeout(function() {
                 fn();
                 timeoutLoop(fn, reps-1, delay);
               }, delay);
}

// pass your function as callback
timeoutLoop(function() { alert("Hi"); },
            5,
            5000);

(我只是快速拼湊起來,所以雖然我相信它可以工作,但可以通過多種方式進行改進,例如,在“循環”中,它可以將索引值傳遞給回調函數,以便您自己的代碼知道哪個迭代這取決於。但希望它能讓你開始。)

我做了一個簡單的同步超時功能。 它以兩種不同的方式工作,回調和非回調。

功能:

function wait(ms, cb) {
  var waitDateOne = new Date();
  while ((new Date()) - waitDateOne <= ms) {
    //Nothing
  }
  if (cb) {
    eval(cb);
  }
}

回調示例:

wait(5000,"doSomething();");

非回調示例:

console.log("Instant!");
wait(5000);
console.log("5 second delay");

這是使用 JQuery doTimeout插件的方法

jQuery('selector').doTimeout( [ id, ] delay, callback [, arg ... ] );

來自文檔:“如果回調返回 true, doTimeout循環將在延遲后再次執行,創建輪詢循環,直到回調返回非 true 值。

 var start = Date.now(); console.log("start: ", Date.now() - start); var i = 0; $.doTimeout('myLoop', 5000, function() { console.log(i+1, Date.now() - start); ++i; return i == 5 ? false : true; });
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-dotimeout/1.0/jquery.ba-dotimeout.min.js"></script>

JavaScript 是單線程的

在javascript中做同步延遲是不可能的,僅僅因為javascript是單線程語言。 瀏覽器(最常見的 JS 運行環境)有所謂的event loop 所以瀏覽器所做的一切都發生在這個循環中。 當您在瀏覽器中執行腳本時,會發生以下情況:

  1. 事件循環調用您的腳本
  2. 逐行執行
  3. 一旦腳本完成*, event loop將繼續運行

請注意,所有這些都發生在event loop的單個幀中! 這意味着在腳本退出之前不會發生其他操作(如渲染、檢查用戶輸入等)。 (*) async JavaScript 例外,例如setTimeout/Interval()requestAnimationFrame() ,它們不在主線程上運行。 所以從event loop來看,腳本已經運行完畢。

這意味着如果 JavaScript 中存在同步延遲,整個瀏覽器將不得不等待延遲結束,同時它什么也做不了。 所以沒有,而且JS中也不會有任何同步延遲。

替代方案 - 也許?

替代方案取決於您想要做的實際事情。 就我而言,我有一個requestAnimationFrame()循環。 所以我需要做的就是存儲時間,並在循環中檢查舊時間和新時間。

let timer =
{
   startTime: 0,
   time: 1000,     // time for the counter in milliseconds
   restart: true   // at the beginning, in order to set startTime
};

loop();
function loop()
{
   if(timer.restart === true)
   {
      timer.startTime = Date.now();
      timer.restart = false;
   }
   
   if((Date.now() - timer.startTime) >= timer.time)
   {
      timer.restart = true;
      console.log('Message is shown every second');
      // here put your logic 
   }

   requestAnimationFrame(loop);
}

受 @andrew65952 啟發,但更現代、更快

function wait(ms) {
  const now = Date.now()
  while (Date.now() - now <= ms) { /* do nothing */}
}

使用函數發生器的解決方案。 表明它可以做到。 不推薦

 function wait(miliseconds){ const gen = function * (){ const end = Date.now() + miliseconds; while(Date.now() < end){yield}; return; } const iter = gen(); while(iter.next().done === false); } console.log("done 0"); wait(1000); console.log("done 1"); wait(2000); console.log("done 2");

節點解決方案

使用 fs.existsSync() 來延遲

const fs = require('fs');
const uuidv4 = require('uuid/v4');

/**
 * Tie up execution for at-least the given number of millis.  This is not efficient.
 * @param millis Min number of millis to wait
 */
function sleepSync(millis) {
    if (millis <= 0) return;
    const proceedAt = Date.now() + millis;
    while (Date.now() < proceedAt) fs.existsSync(uuidv4());
}

fs.existsSync(uuidv4())旨在做一些事情:

  1. 通過生成uuid並尋找不存在的文件來占用線程
  2. 每次新的 uuid 都會破壞文件系統緩存
  3. 查找文件可能是一種優化操作,應該允許其他活動繼續進行(即不固定 CPU)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM