簡體   English   中英

如何同步運行嵌套循環?

[英]How do I run a nested loop synchronously?

我正在寫一個簡單的腳本,一次將一個字符輸出到屏幕上。

我正在這樣做,以便該函數(我稱為slowPrint )可以接收字符串數組。 數組中的每個元素代表一條消息。

這是我到目前為止的代碼:

但是,我沒有得到預期的輸出。 我懷疑這部分是由於代碼的異步性質,盡管我對正在發生的事情以及如何解決它沒有完全清楚的了解。

首先, <br />標簽是在任何消息之前打印的,這告訴我外循環在嵌套的消息甚至開始之前就已經完成了。

但是,當嵌套循環開始時,數組中的每個字符串都將打印一秒,但要完整打印而不是逐字符打印。

我想念什么?

此外,有人可以解釋setTimeout方法的以下行為嗎?

方案1 :當我將第二個參數設置為i * 1000 ,第二個字符串將一秒鍾后打印一次(同樣,整個字符串而不是一個字符一個字符)

  const messages = [ "all systems are operational", "you may proceed" ]; function slowPrint(args) { let screen = document.getElementById('screen'); for (let i = 0; i < args.length; i++) { let message = args[i]; for (let j = 0; j < message.length; j++) { setTimeout(function () { screen.innerHTML += message[j]; }, i * 1000); } screen.innerHTML += '<br />'; } } slowPrint(messages) 
 <div id="screen"></div> 

場景2 :當我將第二個參數設置為j * 1000 ,輸出完全出乎意料:每隔兩個字符以2組為一組打印,但是順序難以理解; 只有最后一個參數的最后一個單詞會按其他所有內容打印。

方案3 :當我將第二個參數設置為1000 ,數組中的所有字符串將在一秒鍾后輸出。

怎么了?

使用async函數和稱為sleep()的幫助器函數將setTimeout()包裝在Promiseawait它,您可以通過最少的更改來完成此操作。

 const messages = [ 'all systems are operational', 'you may proceed' ]; const sleep = ms => new Promise(resolve => { setTimeout(resolve, ms) }) async function slowPrint(args) { let screen = document.getElementById('screen'); for (let i = 0; i < args.length; i++) { let message = args[i]; for (let j = 0; j < message.length; j++) { await sleep(100); screen.innerHTML += message[j]; } screen.innerHTML += '<br />'; } } slowPrint(messages) 
 <div id="screen"></div> 

setTimeout()的回調是異步執行的,因此執行順序將始終如下所示:

// first

setTimeout(function () {
  // at *least* after all the current synchronous code has completely finished
})

// second

如評論中所述,僅實現ECMAScript 2017的 瀏覽器支持async / await

該視頻是js在瀏覽器中的工作方式的最佳解釋之一: 此處

基本上,無論您放置在setTimeout回調中的內容是多少,都將傳遞給第二個參數的毫秒數放到backburner中。 然后將其放入回調隊列中,直到調用堆棧為空,並且它是隊列中的下一項

如果將代碼復制並粘貼到http://latentflip.com/loupe/中,您將看到它實際上如何在后台運行

您可以使用setInterval使用簡潔的代碼來完成此操作。 您只需要適當地管理索引。 此代碼使用i遍歷每個字母,使用j遍歷數組。 i達到極限時, j遞增; j達到極限時,間隔將被清除。

 let screen = document.getElementById('screen'); const messages = [ "all systems are operational", "you may proceed" ]; function slowPrint(args) { let i=0, j = 0 let ivl = setInterval(() => { screen.innerHTML += args[j][i] i++ if (i == args[j].length ){ i = 0; j++ screen.innerHTML += '<br>' } if (j === args.length) clearInterval(ivl) }, 200) } slowPrint(messages) 
 <div id="screen"></div> 

您的代碼有問題的原因是for循環不會停止並等待超時。 for循環使所有超時幾乎同時開始,因此在1000 ms之后它們都將觸發。 當需要定期進行某些操作時,通常使用setInterval是更好的方法。

當然, 還有許多其他方法可以做到這一點。 只是一些更奇特的示例,這是一種使用簡單生成器的方法。 有點難以理解,但是一旦習慣了發電機,看起來就會很干凈:

 const out = document.getElementById('screen') const messages = ["all systems are operational","you may proceed"]; function *iter(messages) { for(m of messages){ for(letter of m) yield letter yield '<br>' } } const gen = iter(messages) const int = setInterval(() => { let n = gen.next() if (n.done) return clearInterval(int) out.innerHTML += n.value }, 100) 
 <div id='screen'></div> 

從使用隊列到使用數學找出范圍,有無數種方法可以做到這一點。 無需過多修改代碼,您只需檢查您是否在最后一個字符處,然后附加一個換行符並使用一個變量來跟蹤輸出的當前時間。

  const messages = [ "all systems are operational", "you may proceed" ]; function slowPrint(args) { let screen = document.getElementById('screen'); let delay = 0; const timeDelay = 100; for (let i = 0; i < args.length; i++) { let message = args[i]; for (let j = 0; j < message.length; j++) { setTimeout(function () { let lineBr = j === message.length - 1 ? '<br>' : '' screen.innerHTML += message[j] + lineBr; }, delay); delay += timeDelay } } } slowPrint(messages) 
 <div id="screen"></div> 

就我個人而言,我將使用更多的隊列,因此無需創建大量的計時器。

 const messages = [ "all systems are operational", "you may proceed" ]; function slowPrint(args) { let screen = document.getElementById('screen'); // combine all the strings into one character array var characters = messages.reduce( function (a, s) { // turn string into an array of characters var letters = s.split('') // last character, add a line break var l = letters.length-1 letters[l] = letters[l] + '<br/>' // append it to our current list return a.concat(letters); }, []); function next() { // append the first character of the array to our output screen.innerHTML += characters.shift() // if we still have more characters, than run it again if (characters.length) window.setTimeout(next, 100); } // kick off the script to output the characters next() } slowPrint(messages) 
 <div id="screen"></div> 

暫無
暫無

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

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