高級前端進階(五)

来源:https://www.cnblogs.com/ywjbokeyuan/archive/2022/05/09/16011512.html
-Advertisement-
Play Games

詳解JavaScript中的事件迴圈機制!!! 一、簡單講解 這個大家應該或多或少都知道的 for (var i = 0; i < 10; i++) { setTimeout(() => { console.log(i); // 輸出10個10 }); } 解析:先執行for迴圈,迴圈疊加i,然後再 ...


詳解JavaScript中的事件迴圈機制!!!

一、簡單講解

這個大家應該或多或少都知道的

for (var i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i); // 輸出10個10
  });
}

解析:先執行for迴圈,迴圈疊加i,然後再執行setTimeout 10遍,所以會輸出10個10
變體:

for (let i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i); // 依次輸出0-9
  });
}

我們知道var跟let本質區別就是作用域的問題,所以會聯想到let導致的,但在這一點上,並不是作用域問題,因為從事件迴圈機制來解釋是有問題的
以上的代碼等價於

for (var i = 0; i < 10; i++) {
  (function (i) {
    setTimeout(() => {
      console.log(i);
    });
  })(i); // 立即執行函數,以上的let應該是將代碼特殊處理了下
}

立即執行函數:函數聲明後立即執行。
從事件迴圈的角度來說,應該先執行for迴圈將i疊加到10,執行完後再去執行for迴圈體(setTimeout),此時的i變成了10,所以每次輸出都是10,
這裡使用let,應該是特殊處理了一下的。

二、深入講解

我們知道JavaScript語言是單線程的,至於為啥是單線程?
假設有兩個線程,一個在頁面上新增一個div,另一個線程在頁面上刪除div,那最終聽誰的?
那JavaScript怎麼實現非同步的呢?
在JavaScript中,有兩類任務:同步任務和非同步任務。
同步任務:普通的任務,依次從上往下執行。
非同步任務:又分為巨集任務、微任務。

巨集任務:setTimeout跟setInterval
微任務:Promise().then() 這裡要註意一下,Promise方法裡面的是同步任務,then裡面的才是微任務
執行順序:先執行同步任務,遇到非同步任務,將其放入到巨集任務或者微任務隊列中,然後優先執行微任務,接下來再去執行巨集任務。

簡單的例子:

setTimeout(function() {
    console.log(1)
}, 0);

new Promise(function(resolve, reject) {
    console.log(2); // 這裡是同步任務
    resolve();
}).then((res) => {
    console.log(3);
})
console.log(4);
// 輸出結果是 2  4  3  1

解析:從上往下執行,setTimeout是巨集任務,放到巨集任務隊列中,
Promise裡面的是同步任務,所以先執行,輸出2,then裡面的是微任務,放到微任務隊列中,
最後一個是同步任務,執行,輸出4,
然後執行微任務,輸出3
最後執行巨集任務,輸出1

來點難度的:

console.log(1);

setTimeout(function () {
  console.log(2);
}, 0);

setTimeout(function () {
  console.log(3);

  setTimeout(function () {
    console.log(4);
  }, 0);
}, 0);

new Promise(function (resolve, reject) {
  console.log(5);
  resolve();
}).then((res) => {
  console.log(6);
  new Promise(function (resolve, reject) {
    console.log(7);
    resolve();
  }).then((res) => {
    console.log(8);
  });
});

new Promise(function (resolve, reject) {
  console.log(9);
  resolve();
})
  .then((res) => {
    console.log(10);
  })
  .then((res) => {
    console.log(11);
  });

console.log(12);
// 輸出 1  5   9   12
//      6  7  10   8   11
//      2  3  4

解析:原理跟上面一樣,不過需要註意的是,8跟11的順序:,在這裡then的層級,將各個Promise裡面第一層的then放在一起,第二層的then放在一起,依此類推,
然後依次執行第一層的,執行完第一層,接下來執行完第二層,依此類推。
第一層(6,7,10)
第二層(8,11)
所以先輸出8, 再輸出11的。

再來一個(也最可能出錯或者無法理解的):

console.log(1);
async function fn() {
  console.log(2);
  await console.log(3);
  console.log(4); // 這一步,你應該會有問題
}
setTimeout(() => {
  console.log(5);
}, 0);
fn();
new Promise((resolve) => {
  console.log(6);
  resolve();
}).then(() => {
  console.log(7);
});
console.log(8);
// 輸出:1  2  3   6   8   4  7  5

解析:async await是Promise的語法糖,只是針對寫法上的優化
將async await翻譯成Promise:

async function fn() {
  console.log(2);
  await console.log(3);
  console.log(4); // 這一步,你應該會有問題
}
// 等價於
function fn() {
  return new Promise((resolve, reject) => {
    console.log(2);
    resolve(
      (() => {
        console.log(3);
      })() // 立即執行函數
    );
  }).then(() => {
    console.log(4);
  });
}

這樣,你應該能夠明白,為啥輸出3之後不是立即輸出4了吧!

再來一道加深鞏固的:

console.log(1);
async function fn() {
  console.log(2);
  await console.log(3);
  await console.log(4);
  console.log(5);
}
setTimeout(() => {
  console.log(6);
}, 2000);
setTimeout(() => {
  console.log(7);
}, 1000);
fn();
new Promise((resolve) => {
  console.log(8);
  new Promise((resolve) => {
    console.log(9);
    resolve();
  }).then(() => {
    console.log(10);
  });
  resolve();
}).then(() => {
  console.log(11);
  new Promise((resolve) => {
    console.log(12);
    resolve();
  }).then(() => {
    console.log(13);
  });
});
console.log(14);
// 輸出 1  2  3  8  9  14
//      4  10 11 12 5  13
//      7  6 

我相信,你現在應該理解掌握了!

只要鑽的深,鐵杵都能給你磨成針!!!

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

-Advertisement-
Play Games
更多相關文章
  • SQL語言基礎 SQL (Structured Query Language:結構化查詢語言) 是用於管理關係資料庫管理系統(RDBMS)。 SQL 的範圍包括數據插入、查詢、更新和刪除,資料庫模式創建和修改,以及數據訪問控制。 本文主要記錄一些主要的SQL命令(比如 SELECT、UPDATE、D ...
  • 一、使用PIVOT實現行轉列 1、首先創建一個學生表 CREATE TABLE [dbo].[Students]( [stu_id] [int] IDENTITY(1,1) NOT NULL, [stu_name] [varchar](100) NULL, [stu_hobby] [varchar] ...
  • 在進行視頻通話過程中,用戶有時候會出現網路不好的情況,比如在進行多人視頻通話或者多人唱歌時,我們需要實時顯示用戶的網路質量。 ...
  • 本次上新,共計新增94個開源組件。組件涉及工具、網路、UI、圖形、音視頻等多種功能。 ...
  • 5月11日晚19:00,Hello HarmonyOS進階系列應用篇第二課《電腦視覺》直播如約而至。 ...
  • 隨著新技術的不斷演進,人工智慧已經廣泛地應用到教育、金融、物流、零售、交通、醫療等各個領域。而在AI高速發展的當下,高效開發變得更為重要,如何將創意想法與AI技術深度融合,迅速轉化為可落地的AI應用,是開發者在激烈競爭中制勝的關鍵。 為了助力開發者快速上線AI業務,為消費者提供創新AI體驗,HMS ...
  • 只有在ajax才能找回一點點主場了,vue中的ajax一天整完,內容還行,主要是對axios的運用。 明天按理說要開始vuex了,這個從來都是只耳聞沒有眼見過,明天來看看看看是個什麼神奇的東西。 一. 解決開發環境ajax跨域問題 1.配置代理(方法一) 首先我們能發起ajax請求的,xhr原生不推 ...
  • 大家好,我是半夏👴,一個剛剛開始寫文的沙雕程式員.如果喜歡我的文章,可以關註➕ 點贊 👍 加我微信:frontendpicker,一起學習交流前端,成為更優秀的工程師~關註公眾號:搞前端的半夏,瞭解更多前端知識! 點我探索新世界! 原文鏈接 ==>http://sylblog.xin/archi ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...