前端读者 | 由setTimeout引发的JS引擎运行机制的研究
本文來自 @xiaoyuze88 鏈接:http://xiaoyuze88.github.io/
太久沒碰代碼了,那天想到關于循環調用setTimeout實現每隔一秒輸出遞增的數的那個問題,搞了搞,發現很多概念模糊了,在此總結下。
所謂的循環調用setTimeout實現遞增輸出,就是說用for循環10次,每隔一秒輸出一個從0~9的數。
不多說,直接上最終代碼再說,細節后面再談。
for (var i = 0; i < 10; i++) {//這里用閉包,為每一個i生成一個獨立的上下文環境,傳遞給里面的console.log,而不會受到setTimeout延時而影響(function (i) {`setTimeout`(function () {console.log(i);}, 1000 * i)})(i); }這里主要的問題在:
首先閉包,這里就不多說了,在這里,閉包的作用就是給閉包內的函數生成一個不受外面環境干擾的上下文環境,由于js的作用域問題。
如果這里不用閉包,寫成諸如:
for (var i = 0; i < 10; i++) {`setTimeout`(function () {console.log(i);}, i * 1000); }會發現,每隔一秒鐘,輸出一個10。
這是由于這一個for循環的執行,瞬間就完成了,也就是說,瞬間注冊了10個延時執行的函數,每一個隔一秒鐘執行。
當注冊的時間點到來,開始執行setTimeout中的語句,由于定義域的問題,此時console.log(i)的這個i指向的是已經到達10的for循環中的i,這就是為什么要用閉包來給setTimeout設置獨立的上下文環境,而避免需要訪問i時訪問到了外面的變量。
另外,如果細想一下,會發現setTimeout的工作過程多少讓人有點迷惑,到底setTimeout等延時類函數在瀏覽器中是如何運作的?這就牽扯到下一個問題,關于瀏覽器中是如果運作的問題。
瀏覽器中,JS引擎是單線程的,假設一個瀏覽器中有三個常駐線程,既JS引擎線程、渲染線程、事件觸發線程,還有處理完即結束的線程如AJAX異步請求。
其中,JS線程與渲染線程是互斥的,這是為了避免JS控制DOM時與頁面渲染發生沖突。而對于JS線程,它是由事件驅動的,由于單線程,所有任務依隊列排序。如果頁面上觸發了事件,如onclick=function(){}、或者由setTimeout添加了一個函數、ajax請求返回的事件等,所有新添加的任務位于隊尾等待處理。
由于是單線程,如果線程被阻塞,如while(true){}死循環,則一切新添加的任務都將被阻塞。
由上面所述,就可以理解為什么setTimeout或setInterval設置的延時事件并不是真是函數處理的延時時間,既setTimout(code,1000)并不是一定會在1秒后處理,這段代碼發生的僅僅是在1秒后,將待處理函數排與js任務隊列末尾。
轉載于:https://www.cnblogs.com/chenrf/p/10106433.html
總結
以上是生活随笔為你收集整理的前端读者 | 由setTimeout引发的JS引擎运行机制的研究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用django2.1开发公司官网(上)
- 下一篇: selenium基础框架的封装(Pyth