你不知道的Javascript:有趣的setTimeout

来源:https://www.cnblogs.com/depsi/archive/2018/01/31/8390507.html
-Advertisement-
Play Games

你不知道的Javascript:有趣的setTimeout 有時候,小小的細節往往隱藏著大大的智慧今天在回顧JavaScript進階用法的時候,發現一個有趣的問題,話不多說,先上代碼: for(var j=0;j<10;j++){ setTimeout(function(){console.l ...


你不知道的Javascript:有趣的setTimeout

有時候,小小的細節往往隱藏著大大的智慧
今天在回顧JavaScript進階用法的時候,發現一個有趣的問題,話不多說,先上代碼:

for(var j=0;j<10;j++){
setTimeout(function(){console.log(j)},5000)
}

看到這三行代碼,也許你會不耐煩道:又要講閉包?要吐了好麽?別急,讓我們先來思考一下,這段代碼在瀏覽器中的執行結果是什麼?

甲:順序列印0到9?

乙:這題我見過,列印十個10!

哪個答案正確?我們繼續上圖:

執行結果顯示,瀏覽器列印出了十個10(因為圖片處理的原因,按下回車到列印之前其實間隔了5秒左右),貌似乙勝出了。但如果你足夠細心,你會發現幾個問題:

  1. 為什麼會迴圈列印十個10而不是0到9?
  2. 從結果來看,for迴圈執行完跳出之後,才開始執行setTimeout(所以j才等於10),為什麼不是每次迭代都執行一次setTimeout呢?

如果上述三個問題你都能回答上來,恭喜你,你已經開始掌握了JavaScript深層次的知識,如果不能,那就乖乖往下看吧!

為什麼會迴圈列印十個10

許多人習慣用第二個問題中的執行結果來回答這個問題:“for迴圈執行完跳出之後,才開始執行setTimeout,所以才列印了十個10”。這樣的答案,只能說是既應付了自己,又應付了別人。其實,要解答第一個問題,首先要解答的就是第二的問題。

為什麼不是每次迭代都執行一次setTimeout

大家都知道,JavaScript在ES6出現以前,是沒有塊狀作用域的,這就意味著, 在for迴圈中用var定義的變數j,其實是屬於全局的,即在全局範圍內都可以被訪問到,既然如此,那其實整個全局作用域中就只有一個j,每次for迴圈都i是在更新這個j。

那麼現在關鍵的問題在於,為什麼整個for迴圈會先於setTimeout執行,而不是我們正常理解的,一次迭代執行一次。

這就涉及到了JavaScript的核心特性:單線程。

JavaScript設計的初衷,是瀏覽器用來與用戶進行交互和DOM操作的。這就決定了它必須是單線程的,設想JavaScript同事有兩個線程,一個線程在DOM節點添加內容,一個線程刪除該節點,瀏覽器就會出現混亂。所以,為了避免複雜性,從一誕生,JavaScript就是單線程,這已經成了這門語言的核心特征,將來也不會改變。

單線程就意味著,所有任務需要排隊,前一個任務結束,才會執行後一個任務。如果前一個任務耗時很長,後一個任務就不得不一直等著。

為了優化單線程的性能,JavaScript將任務分成兩種,一種是同步任務(synchronous),另一種是非同步任務(asynchronous)。同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;非同步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有主線程中的同步任務執行完畢,非同步任務才會進入執行隊列執行。只要主線程空了,就會去讀取"任務隊列",這就是JavaScript的運行機制。這個過程會不斷重覆。

而setTimeout,就被JavaScript定義為非同步任務。每次for迴圈的迭代,都將setTimeout中的回調函數加入任務隊列等待執行。也就是說,只有同步任務中的for迴圈完全結束,主線程中才會去任務隊列中找到尚未執行的十個setTimeout(十次迭代)回調函數並順序執行(先進先出)。而此時,i已經經過迴圈結束變成了10,所以,此時主線程執行的,是十個一摸一樣的列印i的回調函數,即列印十個10。至此就完美回答了第一和第二個問題,文章開頭的代碼與下麵的代碼其實是等價的:

for(var i=0;i<10;i++){}
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)

小小的一個setTimeout,牽扯出了很多JavaScript的深層次問題,雖然總結成一篇文章只有區區數百字,但是我在成文的過程中查閱了大量的資料,也做了許多實驗。

最後,給出一個很小但是仍然在困擾我的一個問題,希望有興趣的小伙伴可以跟我一起研究:

setTimeout(function(){while(true){}},6000);
setTimeout(function(){console.log(1)},10000);
setTimeout(function(){console.log(2)},5000);

上述代碼的執行順序是怎樣的?setTimeout的定時,是定時插入執行棧之後立即執行,還是立即插入執行棧定時執行?

期待大家的留言。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • mysql max 與 where 間的執行問題 執行sql: CREATE TABLE `grades` ( `id` int(20) NOT NULL AUTO_INCREMENT, `student_id` int(20) NOT NULL, `subject` varchar(20) COL ...
  • 條件過濾 我們需要看第一季度的數據是怎樣的,就需要使用條件過濾 體感的舒適適濕度是40-70,我們試著過濾出體感舒適濕度的數據 最後整合上面兩種條件,在一季度體感濕度比較舒適的數據 列排序 數據按照某列進行排序 “by”參數可以使用字元串,也可以是列表,ascending 的參數也可以是單個值或者列 ...
  • 一,微信公眾平臺。 1,“再小的個體,也有自己的品牌”,這是微信公眾平臺的官方廣告。 2,微信公眾平臺沒有認證門檻,只需要一個郵箱和手持身份證照片。目前一個身份證號只可註冊兩個微信公眾帳號。 二,LBS的開發。 1,LBS基於位置的服務(Location Based Service). 2,由於安卓 ...
  • 造成這個問題的原因是在xml文件中出現了重覆依賴,何為重覆依賴,如下: 以上便叫重覆依賴 轉載請標明出處:http://www.cnblogs.com/tangZH/p/8386978.html ...
  • 隨著 "Hybrid APP" 的流行,對其調試變得必不可少。使用Xcode我們能看到的僅僅是WebView,要想進一步查看裡面的a標簽、button和其他元素,Xcode是心有餘而力不足。但是不用擔心,Safari的調試功能能夠彌補Xcode的不足,讓你能夠更方便地調試此類APP。要想使用Safa ...
  • 1.在SystemServer中啟動PackageManagerService.main 2.newPackageManagerService()並添加到ServiceManager中 3.newinstaller(建立installer和installd的socket連接,最終在底層實現insta ...
  • 上面類是AmS的全稱,另外兩大核心功能是WindowManagerService.java和View.java AmS提供的主要功能: AmS中定義了幾個重要的數據類,分別用來保存進程(Process)、活動(Activity)和任務(Task) ProcessRecord.java記錄的進程的相關 ...
  • 之前有寫過一個圖表lib,但是開發的速度,大多很難跟上產品需求變化的腳步,所以修改了下原先的圖表庫,支持圖表下麵能整合table顯示對應的類目,用曲線替換了折線,支持多曲線的顯示,增加了顯示的動畫,,增加了一些可定製的屬性,支持水平柱狀圖和疊加柱狀圖,以及多曲線圖和餅狀圖的顯示 1.效果圖 2.各種 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...