最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript單線程實現(xiàn)異步的詳細代碼示例

 更新時間:2025年08月06日 08:28:32   作者:超級無敵大蟑王  
瀏覽器JavaScript的作用是操作DOM,這就決定了它只能是單線程的,否則會帶來很復雜的問題,這篇文章主要介紹了JavaScript單線程實現(xiàn)異步的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下

1.瀏覽器內核:

在講JavaScript異步之前,先講一下JavaScript運行環(huán)境,因為JavaScript是否能實現(xiàn)異步是通過運行環(huán)境機制決定的,我們經常使用的環(huán)境就是瀏覽器環(huán)境了,所以我今天主要講一下在瀏覽器的渲染進程(瀏覽器內核)如何執(zhí)行異步的。

負責頁面的渲染,腳本的執(zhí)行和時間處理,每一個tab頁也都表示一個進程

對于渲染進程來說,它其實就是多線程的:

  • GUI渲染線程:負責頁面渲染解析 HTML/CSS 生成 DOM 樹和 CSSOM 樹等
  • JS引擎線程:負責解析和執(zhí)行JavaScript腳本程序,并且一個進程只有一個js引擎線程,js引擎是一個單線程
  • 事件觸發(fā)線程:用來控制事件循環(huán)(click,setTimeout,ajax等),當事件滿足條件的時候,將事件放到js引擎所在的執(zhí)行隊列中
  • 定時觸發(fā)器線程:setInterval與setTimeout所在的線程, 定時任務并不是由JS引擎計時的,是由定時觸發(fā)線程來計時的,執(zhí)行完畢會通知事件觸發(fā)進程
  • 異步http請求線程:處理ajax請求,當完成后,通過回調函數觸發(fā)事件觸發(fā)線程

GUI渲染線程和JS引擎線程互斥:

當JS引擎線程執(zhí)行時GUI渲染線程會被掛起,GUI更新則會被保存在一個隊列中等待JS引擎線程空閑時立即被執(zhí)行,防止渲染結果不可預期。

2.單線程:

javascript是單線程的,說明在任何一個時間點,JavaScript 的主線程(也就是它的調用棧)只能執(zhí)行一件任務。如果前一個任務沒執(zhí)行完,后一個任務就必須排隊等待。

如果是這樣就會導致異步阻塞,很多任務沒有辦法瞬時完成,比如:

  • 網絡請求:向服務器請求數據,有時可能需要幾秒
  • 定時器:需要到指定時間才能執(zhí)行
  • 用戶事件:等待用戶的交互,發(fā)生時間不確定

3.異步:

異步就是為了解決單線程的問題,可以實現(xiàn)異步代碼不阻塞,當觸發(fā)到異步代碼的時候,把它放到一遍

瀏覽器內核是如何實現(xiàn)異步呢?

JavaScript本身是單線程的,它實現(xiàn)異步來自于它所運行的環(huán)境—通常是瀏覽器,這個環(huán)境提供了一套復雜的機制,我們可以把它里面的一系列線程和機制策略想象成一個分工明確的團隊。這個團隊由四個核心成員組成:

  • JS 主線程  & 調用棧 :這是 JavaScript 唯一的核心員工。他很勤奮,一次只能專心做一件事,所有同步任務都在他這里排隊執(zhí)行。
  • 宿主環(huán)境 API :這是 JavaScript 的“外包團隊”。瀏覽器提供了很多線程,比如定時器模塊、網絡請求模塊等。這些不占用 JS 主線程的時間,可以在后臺里獨立工作。
  • 任務隊列:這是一個待辦列表。當宿主環(huán)境完成了某項異步任務后(比如定時器時間到了,或者網絡請求成功返回了),他們不會直接把結果交給 JS 主線程(因為主線程可能正在忙),而是把相應的回調函數放到這個待辦列表里排隊。
  • 事件循環(huán):調用棧 -> 微任務 -> 宏任務 -> 渲染

任務隊列又分為兩種:

  • 宏任務隊列:存放 `setTimeout`, `setInterval`, I/O 操作,UI 渲染等任務的回調。我們上面講的“任務隊列”主要指的就是它。
  • 微任務隊列:存放 `Promise.then()`, `async/await` 等任務的回調。

微任務的優(yōu)先級高于宏任務,當微任務和宏任務一起執(zhí)行完畢的時候并且調用棧是空的時候,先執(zhí)行微任務再執(zhí)行宏任務

setTimeout(() => console.log('Timeout (宏任務)'), 0);
Promise.resolve().then(() => console.log('Promise (微任務)'));
console.log('Script (同步)');
// 輸出順序:
// Script (同步)
// Promise (微任務)
// Timeout (宏任務)

4.回調地獄:

當多個相互依賴的異步操作需要按順序執(zhí)行時,開發(fā)者將后一個操作的邏輯寫在了前一個操作的回調函數中,導致函數調用層層嵌套,形成一種橫向擴展、難以閱讀和維護的代碼結構。

回調地獄會導致什么問題:

1. 可讀性極差: 代碼不是從上到下線性執(zhí)行,而是像剝洋蔥一樣,一層包著一層。人的大腦很難快速理清其中的邏輯順序和依賴關系。

2. 難以維護: 如果需求變更,比如要在第二步和第三步之間增加一個新的異步操作,你需要小心翼翼地找到正確的位置,插入新的嵌套層,并調整大量的花括號和縮進。這極易出錯。

3. 錯誤處理復雜: `try...catch` 無法跨越異步邊界捕獲回調函數中的錯誤。你必須在每一個嵌套層級都單獨處理錯誤(就像上面代碼中的 `err1`, `err2`, `err3`),這導致代碼非常冗長和重復。

4. 耦合度高: 每一層的邏輯都和上一層的回調緊緊地耦合在一起,很難將某一步的邏輯抽離出來進行復用。

典型的回調地獄:

console.log('開始!');
setTimeout(() => {
  console.log('1秒過去了'); // 第一個回調
  // 為了保證順序,第二個setTimeout必須嵌套在第一個回調內部
  setTimeout(() => {
    console.log('又2秒過去了'); // 第二個回調
    // 第三個setTimeout必須嵌套在第二個回調內部
    setTimeout(() => {
      console.log('全部完成!'); // 第三個回調
    }, 3000); // 3秒
  }, 2000); // 2秒
}, 1000); // 1秒

解決辦法:

1.使用 `Promise` 鏈式調用:

function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
delay(1000)
  .then(() => {
    console.log('1秒過去了');
    // 返回一個新的Promise,以便繼續(xù)鏈接.then
    return delay(2000); 
  })
  .then(() => {
    console.log('又2秒過去了');
    return delay(3000);
  })
  .then(() => {
    console.log('全部完成!');
  })
  .catch(err => {
    // 統(tǒng)一處理鏈條中任何環(huán)節(jié)可能出現(xiàn)的錯誤
    console.error('出錯了:', err);
  });

2.使用async/await:

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
// 必須在一個 async 標記的函數內部使用 await
async function runTimerSequence() {
  try {
    console.log('開始!');

    await delay(1000); // “等待”1秒,但不會阻塞主線程
    console.log('1秒過去了');

    await delay(2000); // “等待”2秒
    console.log('又2秒過去了');

    await delay(3000); // “等待”3秒
    console.log('全部完成!');

  } catch (err) {
    console.error('出錯了:', err);
  }
}
// 執(zhí)行這個異步函數
runTimerSequence();

總結 

到此這篇關于JavaScript單線程實現(xiàn)異步的文章就介紹到這了,更多相關JS單線程實現(xiàn)異步內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • JS中改變this指向的方法(call和apply、bind)

    JS中改變this指向的方法(call和apply、bind)

    this是javascript的一個關鍵字,隨著函數使用場合不同,this的值會發(fā)生變化。但是總有一個原則,那就是this指的是調用函數的那個對象,通過本文給大家介紹JS中改變this指向的方法(call和apply、bind),需要的朋友參考下
    2016-03-03
  • 刪除table表格行的實例講解

    刪除table表格行的實例講解

    下面小編就為大家?guī)硪黄獎h除table表格行的實例講解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • JavaScript繼承與多繼承實例分析

    JavaScript繼承與多繼承實例分析

    這篇文章主要介紹了JavaScript繼承與多繼承,結合實例形式較為詳細的分析了JavaScript繼承與多繼承的概念、原理、實現(xiàn)與使用方法,需要的朋友可以參考下
    2018-05-05
  • document.forms[].submit()使用介紹

    document.forms[].submit()使用介紹

    這篇文章主要介紹了document.forms[].submit()使用,需要的朋友可以參考下
    2014-02-02
  • JS簡單的輪播的圖片滾動實例

    JS簡單的輪播的圖片滾動實例

    JS簡單的輪播的圖片滾動實例,需要的朋友可以參考一下
    2013-06-06
  • JavaScript實現(xiàn)多行文本溢出

    JavaScript實現(xiàn)多行文本溢出

    這篇文章主要為大家詳細介紹了如何使用JavaScript實現(xiàn)多行文本溢出省略號的樣式,文中的示例代碼講解詳細,有需要的小伙伴可以參考一下
    2024-03-03
  • JS替換字符串中指定位置的字符(多種方法)

    JS替換字符串中指定位置的字符(多種方法)

    這篇文章主要介紹了js替換字符串中指定位置的字符,本文通過兩種方法實例代碼相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-05-05
  • js繼承的實現(xiàn)代碼

    js繼承的實現(xiàn)代碼

    學著js的繼承,自己也寫了個。感覺不是很好?,F(xiàn)在很多代碼都封裝成類。
    2010-08-08
  • js替換字符串的所有示例代碼

    js替換字符串的所有示例代碼

    本文為大家詳細介紹下js如何替換字符串中所有,肯定包含了你所想要的,具體的實現(xiàn)思路及代碼如下,感興趣的朋友可以參考下哈,希望對大家有所幫助
    2013-07-07
  • xtree.js 代碼

    xtree.js 代碼

    xtree.js 代碼...
    2007-03-03

最新評論

淮北市| 两当县| 泰顺县| 城口县| 通州区| 房产| 元江| 奈曼旗| 玉田县| 乌鲁木齐市| 同江市| 宁国市| 常州市| 多伦县| 江城| 英山县| 肇东市| 乌拉特中旗| 大庆市| 黔东| 岳阳县| 姜堰市| 太保市| 临洮县| 辉县市| 焉耆| 黔东| 怀化市| 如皋市| 舟曲县| 清流县| 扶风县| 新邵县| 霍城县| 郧西县| 克山县| 南雄市| 乌鲁木齐县| 建瓯市| 海丰县| 吴旗县|