欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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)文章

  • JavaScript中函數(shù)的四種調(diào)用方式總結(jié)

    JavaScript中函數(shù)的四種調(diào)用方式總結(jié)

    這篇文章主要為大家詳細(xì)介紹了JavaScript中函數(shù)的四種調(diào)用方式,文中的示例代碼講解詳細(xì),對(duì)我們深入掌握J(rèn)avaScript有一定的幫助,需要的可以參考下
    2023-10-10
  • 微信小程序?qū)崿F(xiàn)tab切換效果

    微信小程序?qū)崿F(xiàn)tab切換效果

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)tab切換效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • JS將所有對(duì)象s的屬性復(fù)制給對(duì)象r(原生js+jquery)

    JS將所有對(duì)象s的屬性復(fù)制給對(duì)象r(原生js+jquery)

    這篇文章主要介紹了js中將所有對(duì)象s的屬性復(fù)制給對(duì)象r的方法,原生js+jquery分別實(shí)現(xiàn)
    2014-01-01
  • javascript使用正則實(shí)現(xiàn)去掉字符串前面的所有0

    javascript使用正則實(shí)現(xiàn)去掉字符串前面的所有0

    這篇文章主要介紹了javascript使用正則實(shí)現(xiàn)去掉字符串前面的所有0,需要的朋友可以參考下
    2018-07-07
  • Node調(diào)試工具JSHint的安裝及配置教程

    Node調(diào)試工具JSHint的安裝及配置教程

    Node的優(yōu)勢我就不再亂吹捧了,它讓javascript統(tǒng)一web的前后臺(tái)成為了可能。但是對(duì)于新手來說,server端的JS代碼可能不像client端的代碼那么好調(diào)試,直觀。client端JS代碼的調(diào)試基本上經(jīng)歷了一個(gè)從“肉眼--alert()--firebug(或者其它的developer tools)”的一個(gè)過程。而對(duì)于server端的調(diào)試,可能新手仍然停留在使用“肉眼--console()”的階段。其實(shí),Node經(jīng)過了這么多年(雖然才短短幾年)的發(fā)展,也有了很多不錯(cuò)的第三方的調(diào)試工具。包括Node內(nèi)建的調(diào)試工具debugger、node-inspector等。
    2014-05-05
  • 輕松學(xué)習(xí)Javascript閉包

    輕松學(xué)習(xí)Javascript閉包

    閉包(closure)是Javascript語言的一個(gè)難點(diǎn),也是它的特色,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)。這篇文章主要介紹了Javascript閉包,需要的朋友可以參考下
    2017-03-03
  • 頁面載入結(jié)束自動(dòng)調(diào)用js函數(shù)示例

    頁面載入結(jié)束自動(dòng)調(diào)用js函數(shù)示例

    當(dāng)頁面加載完成后自動(dòng)調(diào)用預(yù)先編好的js函數(shù),在某些特殊情況下還是比較實(shí)用的,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下
    2013-09-09
  • 關(guān)于javascript解決閉包漏洞的一個(gè)問題詳解

    關(guān)于javascript解決閉包漏洞的一個(gè)問題詳解

    閉包在JavaScript高級(jí)程序設(shè)計(jì)(第3版)中是這樣描述:閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù),下面這篇文章主要給大家介紹了關(guān)于javascript解決閉包漏洞的一個(gè)問題的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • JS實(shí)現(xiàn)可直接顯示網(wǎng)頁代碼運(yùn)行效果的HTML代碼預(yù)覽功能實(shí)例

    JS實(shí)現(xiàn)可直接顯示網(wǎng)頁代碼運(yùn)行效果的HTML代碼預(yù)覽功能實(shí)例

    這篇文章主要介紹了JS實(shí)現(xiàn)可直接顯示網(wǎng)頁代碼運(yùn)行效果的HTML代碼預(yù)覽功能,通過獲取文本框內(nèi)容并在新窗口打印輸出來實(shí)現(xiàn)直接運(yùn)行html代碼的功能,簡單實(shí)用,需要的朋友可以參考下
    2015-08-08
  • 利用prop-types第三方庫對(duì)組件的props中的變量進(jìn)行類型檢測

    利用prop-types第三方庫對(duì)組件的props中的變量進(jìn)行類型檢測

    本篇文章主要介紹了利用prop-types第三方庫對(duì)組件的props中的變量進(jìn)行類型檢測的相關(guān)知識(shí),具有很好的參考價(jià)值。下面跟著小編一起來看下吧
    2017-05-05

最新評(píng)論