[英]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()
包裝在Promise
並await
它,您可以通過最少的更改來完成此操作。
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.