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

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

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

1.瀏覽器內(nèi)核:

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

負(fù)責(zé)頁面的渲染,腳本的執(zhí)行和時(shí)間處理,每一個(gè)tab頁也都表示一個(gè)進(jìn)程

對(duì)于渲染進(jìn)程來說,它其實(shí)就是多線程的:

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

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

當(dāng)JS引擎線程執(zhí)行時(shí)GUI渲染線程會(huì)被掛起,GUI更新則會(huì)被保存在一個(gè)隊(duì)列中等待JS引擎線程空閑時(shí)立即被執(zhí)行,防止渲染結(jié)果不可預(yù)期。

2.單線程:

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

如果是這樣就會(huì)導(dǎo)致異步阻塞,很多任務(wù)沒有辦法瞬時(shí)完成,比如:

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

3.異步:

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

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

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

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

任務(wù)隊(duì)列又分為兩種:

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

微任務(wù)的優(yōu)先級(jí)高于宏任務(wù),當(dāng)微任務(wù)和宏任務(wù)一起執(zhí)行完畢的時(shí)候并且調(diào)用棧是空的時(shí)候,先執(zhí)行微任務(wù)再執(zhí)行宏任務(wù)

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

4.回調(diào)地獄:

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

回調(diào)地獄會(huì)導(dǎo)致什么問題:

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

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

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

4. 耦合度高: 每一層的邏輯都和上一層的回調(diào)緊緊地耦合在一起,很難將某一步的邏輯抽離出來進(jìn)行復(fù)用。

典型的回調(diào)地獄:

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

解決辦法:

1.使用 `Promise` 鏈?zhǔn)秸{(diào)用:

function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
delay(1000)
  .then(() => {
    console.log('1秒過去了');
    // 返回一個(gè)新的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)的錯(cuò)誤
    console.error('出錯(cuò)了:', err);
  });

2.使用async/await:

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

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

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

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

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

總結(jié) 

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

相關(guān)文章

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

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

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

    刪除table表格行的實(shí)例講解

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

    JavaScript繼承與多繼承實(shí)例分析

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

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

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

    JS簡單的輪播的圖片滾動(dòng)實(shí)例

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

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

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

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

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

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

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

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

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

    xtree.js 代碼

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

最新評(píng)論

莱西市| 温州市| 青冈县| 礼泉县| 新乡县| 灵川县| 柳河县| 普宁市| 沂源县| 潜江市| 新巴尔虎右旗| 呼图壁县| 霍州市| 开平市| 海兴县| 余庆县| 通渭县| 吉安县| 平和县| 潞西市| 舒城县| 布拖县| 板桥市| 闽侯县| 镇沅| 华宁县| 长葛市| 翼城县| 沂南县| 雷波县| 定日县| 双峰县| 汽车| 西乡县| 务川| 荥阳市| 壤塘县| 临澧县| 萨迦县| 叶城县| 独山县|