這篇文章主要介紹了從setTimeout看js函數執行過程,需要的朋友可以參考下
老實說,寫這篇文章的時候心裡是有點壓抑的,因為受到打擊了,為什麼?就 因為喜歡折騰不小心看到了這個"簡單"的函數:
for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i) }, i * 1000); } console.log(i);
什麼?這不就是我很久之前看到的先打印一個5,再打印一個5,之後每隔一秒就打印一個5,直到打印完6個5的實現方法嗎?那麼問題來了,如果我要依序列印0,1,2,3,4,5的話我該怎麼辦,其實在這之前我就知道有這兩個方法:一個是這樣:
function log(i){ setTimeout(function(){ console.log(i) },i*1000) }; for (var i = 0; i < 5; i++) { log(i) ; } console.log(i);
還有一個是這樣:
for(var i=0;i<5;i++){ (function(e){ setTimeout(function(){ console.log(e) },i*1000); })(i); }; console.log(i);
不怕笑話,在這之前我是沒搞懂這兩個函數真正意義上的作用是用來幹嘛的,只強迫自己這樣記住這樣修改就可以了,但是現在不行啊,我有強迫症啊!於是,我慢慢分析了一下,發現上面那段程式碼可以分離成這樣:
i=0時;滿足條件;
setTimeout(function(){ console.log(i) },0*1000);
i=1時;滿足條件;
setTimeout(function(){ console.log(i) },1*1000);
i=2時;滿足條件;
setTimeout(function(){ console.log(i) },2*1000);
i=3時;滿足條件;
setTimeout(function(){ console.log(i) },3*1000);
i=4時;滿足條件;
setTimeout(function(){ console.log(i) },4*1000);
i=5時,不滿足條件,跳出循環,接著執行for循環後面的console.log(i),打印5;最後依次每秒打印5;
真有意思,為什麼setTimeout裡面的console. log會是後於for迴圈外面的console.log執行呢?直到我認出了這個單字=>"隊列", 隊列又有宏任務隊列(Macro Task)以及微任務隊列(Micro Task)之分,在javascript中:
macro-task包括:script (整體程式碼), setTimeout , setInterval, setImmediate, I/O, UI rendering。
micro-task包含:process.nextTick, Promises , Object.observe, MutationObserver
上面函數的setTimeout就屬於巨集任務
在js中,事件循環的順序是從script開始第一次循環,隨後全域上下文進入函數呼叫棧,碰到macro-task就將其交給處理它的模組處理完之後將回呼函數放進macro-task的隊列之中,碰到micro -task也是將其回呼函數放進micro-task的佇列之中。直到函式呼叫堆疊清空只剩下全域執行上下文,然後開始執行所有的micro-task。當所有可執行的micro-task執行完畢之後。迴圈再次執行macro-task中的一個任務佇列 ,執行完之後再執行所有的micro-task,就這樣一直循環。
這就是為什麼setTimeout裡面的console.log會是後於for循環外面的console.log執行,在函數執行上下文中,seiTimeout函數會被放到處理他的macro-task的隊列之中,所以循環的時候setTimeout裡面的function是不會被執行的,而是等到所有整體代碼(非隊列)跑完之後才會執行隊列中的函數;寫到這裡,可能會有點懵逼,其實我也有點懵逼,哈哈哈! !
為了加深理解,還可以試試在裡面加入Promise,於是就有了這個:
(function copy() { setTimeout(function() {console.log(4)}, 0); new Promise(function executor(resolve) { console.log(1); for( var i=0 ; i<10000 ; i++ ) { i == 9999 && resolve(); } console.log(2); }).then(function() { console.log(5); }); console.log(3); })()
解釋一下=>
1.首先,script任務來源先執行,全域上下文入堆疊。
2.script任務來源的程式碼在執行時遇到setTimeout,作為一個macro-task,將其回呼函數放入自己的佇列之中。
3.script任務來源的程式碼在執行時遇到Promise實例。 Promise建構函式中的第一個參數是在目前任務直接執行不會被放入佇列之中,因此此時輸出 1 。
4.在for迴圈裡面遇到resolve函數,函數入棧執行後出棧,此時Promise的狀態變成Fulfilled。程式碼接著執行遇到console.log(2),輸出2。
5.接著執行,程式碼遇到then方法,其回呼函數作為micro-task入棧,進入Promise的任務隊列之中,此時Promise的then 裡面的function回呼函數跟setTimeout裡面的function回呼函數有著異曲同工之意,都會被放到各自的任務隊列中,
直到函數上下文即script中所有的非隊列代碼執行完畢後再執行,而且微任務隊列優先於宏任務隊列被處理,
總體順序為:上下文非隊列代碼>微任務隊列回呼函數代碼>宏任務隊列回調函數代碼
6.代碼接著執行,此時遇到console. log(3),輸出3。
7.輸出3之後第一個巨集任務script的程式碼執行完畢,這時候開始開始執行所有在佇列之中的micro-task。 then的回呼函數入棧執行完畢之後出棧,這時候輸出5
8.這時候所有的micro-task執行完畢,第一輪迴圈結束。第二輪迴圈從setTimeout的任務佇列開始,setTimeout的回呼函數入棧執行完畢之後出棧,此時輸出4。
最後,為了加深理解,再上一段程式碼:
console.log('golb1'); setTimeout(function() { console.log('timeout1'); new Promise(function(resolve) { console.log('timeout1_promise'); resolve(); setTimeout(function(){ console.log('time_timeout') }); }).then(function() { console.log('timeout1_then') }) setTimeout(function() { console.log('timeout1_timeout1'); }); }) new Promise(function(resolve) { console.log('glob1_promise'); resolve(); setTimeout(function(){ console.log('prp_timeout') }); }).then(function() { console.log('glob1_then') })
如果你的執行結果是:golb1=>glob1_promise=>glob1_then=>timeout1=>timeout1_promise=> ;timeout1_then=>prp_timeout=>time_timeout=>timeout1_timeout1,
可能非同步佇列算是入門了吧! ~~上面的程式碼看起來有點雜亂,可能用asyns搭配await改造一下會更好,但是這或多或少是鄙人從setTimeout中得到的見解吧
上面是我整理給大家的,希望今後會對大家有幫助。
相關文章:
以上是在JS函數中有關setTimeout詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript起源於1995年,由布蘭登·艾克創造,實現語言為C語言。 1.C語言為JavaScript提供了高性能和系統級編程能力。 2.JavaScript的內存管理和性能優化依賴於C語言。 3.C語言的跨平台特性幫助JavaScript在不同操作系統上高效運行。

JavaScript在瀏覽器和Node.js環境中運行,依賴JavaScript引擎解析和執行代碼。 1)解析階段生成抽象語法樹(AST);2)編譯階段將AST轉換為字節碼或機器碼;3)執行階段執行編譯後的代碼。

Python和JavaScript的未來趨勢包括:1.Python將鞏固在科學計算和AI領域的地位,2.JavaScript將推動Web技術發展,3.跨平台開發將成為熱門,4.性能優化將是重點。兩者都將繼續在各自領域擴展應用場景,並在性能上有更多突破。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

Dreamweaver Mac版
視覺化網頁開發工具

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3 Linux新版
SublimeText3 Linux最新版

Atom編輯器mac版下載
最受歡迎的的開源編輯器

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具