成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

搞懂 JavsScript 異步 —? 事件輪詢

adam1q84 / 2460人閱讀

想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你!

JavsScript 是一門單線程的編程語言,這就意味著一個時間里只能處理一件事,也就是說 JavaScript 引擎一次只能在一個線程里處理一條語句。

雖然單線程簡化了編程代碼,因為你不必太擔心并發(fā)引出的問題,這也意味著你將在阻塞主線程的情況下執(zhí)行長時間的操作,如網(wǎng)絡(luò)請求。

想象一下從API請求一些數(shù)據(jù),根據(jù)具體的情況,服務(wù)器需要一些時間來處理請求,同時阻塞主線程,使網(wǎng)頁長時間處于無響應(yīng)的狀態(tài)。

這就是引入異步 JavaScript 的原因。使用異步 JavaScript(如 回調(diào)函數(shù)、promise、async/await),可以不用阻塞主線程的情況下長時間執(zhí)行網(wǎng)絡(luò)請求 :)

可能你知道不知道 異步 JavsScript 是如何工作,并不要緊,但知道它是如何工作,對 JavaScript 異步更深入的了解是有幫助的。

所以不在啰嗦了,我們開始吧 :)

同步JavaScript是如何工作的?

在深入研究異步JavaScript之前,讓我們首先了解同步 JavaScript 代碼如何在 JavaScript 引擎中執(zhí)行。例如:

    const second = () => {
      console.log("Hello there!");
    }
    
    const first = () => {
      console.log("Hi there!");
      second();
      console.log("The End");
    }
    
    first();

要理解上述代碼如何在 JavaScript 引擎中執(zhí)行,我們必須理解執(zhí)行上下文和調(diào)用堆棧(也稱為執(zhí)行堆棧)的概念。

函數(shù)代碼在函數(shù)執(zhí)行上下文中執(zhí)行,全局代碼在全局執(zhí)行上下文中執(zhí)行。每個函數(shù)都有自己的執(zhí)行上下文。

調(diào)用棧

調(diào)用堆棧顧名思義是一個具有LIFO(后進先出)結(jié)構(gòu)的堆棧,用于存儲在代碼執(zhí)行期間創(chuàng)建的所有執(zhí)行上下文。

JavaScript 只有一個調(diào)用棧,因為它是一種單線程編程語言。調(diào)用堆棧具有 LIFO 結(jié)構(gòu),這意味著項目只能從堆棧頂部添加或刪除。

讓我們回到上面的代碼片段,并嘗試理解代碼如何在JavaScript引擎中執(zhí)行。

const second = () => {
  console.log("Hello there!");
}
const first = () => {
  console.log("Hi there!");
  second();
  console.log("The End");
}
first();




這里發(fā)生了什么?

當執(zhí)行此代碼時,將創(chuàng)建一個全局執(zhí)行上下文(由main()表示)并將其推到調(diào)用堆棧的頂部。當遇到對first()的調(diào)用時,它會被推送到堆棧的頂部。

接下來,console.log("Hi there!")被推送到堆棧的頂部,當它完成時,它會從堆棧中彈出。之后,我們調(diào)用second(),因此second()函數(shù)被推到堆棧的頂部。

console.log("Hello there!")被推送到堆棧頂部,并在完成時彈出堆棧。second() 函數(shù)結(jié)束,因此它從堆棧中彈出。

console.log(“the End”)被推到堆棧的頂部,并在完成時刪除。之后,first()函數(shù)完成,因此從堆棧中刪除它。

程序在這一點上完成了它的執(zhí)行,所以全局執(zhí)行上下文(main())從堆棧中彈出。

異步JavaScript是如何工作的?

現(xiàn)在我們已經(jīng)對調(diào)用堆棧和同步JavaScript的工作原理有了基本的了解,讓我們回到異步JavaScript

阻塞是什么?

讓我們假設(shè)我們正在以同步的方式進行圖像處理或網(wǎng)絡(luò)請求。例如:

const processImage = (image) => {
  /**
  * doing some operations on image
  **/
  console.log("Image processed");
}
const networkRequest = (url) => {
  /**
  * requesting network resource
  **/
  return someData;
}
const greeting = () => {
  console.log("Hello World");
}
processImage(logo.jpg);
networkRequest("www.somerandomurl.com");
greeting();

做圖像處理和網(wǎng)絡(luò)請求需要時間,當processImage()函數(shù)被調(diào)用時,它會根據(jù)圖像的大小花費一些時間。

processImage() 函數(shù)完成后,將從堆棧中刪除它。然后調(diào)用 networkRequest() 函數(shù)并將其推入堆棧。同樣,它也需要一些時間來完成執(zhí)行。

最后,當networkRequest()函數(shù)完成時,調(diào)用greeting()函數(shù),因為它只包含一個控制臺。日志語句和控制臺。日志語句通常很快,因此greeting()函數(shù)立即執(zhí)行并返回。

因此,我們必須等待函數(shù)(如processImage()networkRequest())完成。這意味著這些函數(shù)阻塞了調(diào)用堆?;蛑骶€程。因此,在執(zhí)行上述代碼時,我們不能執(zhí)行任何其他操作,這是不理想的。

那么解決辦法是什么呢?

最簡單的解決方案是異步回調(diào)。我們使用異步回調(diào)使代碼非阻塞。例如:

const networkRequest = () => {
  setTimeout(() => {
    console.log("Async Code");
  }, 2000);
};
console.log("Hello World");
networkRequest();

這里我使用了setTimeout方法來模擬網(wǎng)絡(luò)請求。請記住setTimeout不是JavaScript引擎的一部分,它是web api(在瀏覽器中)和C/ c++ api(在node.js中)的一部分。

為了理解這段代碼是如何執(zhí)行的,我們必須理解更多的概念,比如事件輪詢和回調(diào)隊列(或消息隊列)。

事件輪詢、web api和消息隊列不是JavaScript引擎的一部分,而是瀏覽器的JavaScript運行時環(huán)境或Nodejs JavaScript運行時環(huán)境的一部分(對于Nodejs)。在Nodejs中,web api被c/c++ api所替代。

現(xiàn)在讓我們回到上面的代碼,看看它是如何異步執(zhí)行的。

const networkRequest = () => {
  setTimeout(() => {
    console.log("Async Code");
  }, 2000);
};

console.log("Hello World");

networkRequest();

console.log("The End");

當上述代碼在瀏覽器中加載時,console.log(" Hello World ") 被推送到堆棧中,并在完成后彈出堆棧。接下來,將遇到對 networkRequest() 的調(diào)用,因此將它推到堆棧的頂部。

下一個 setTimeout() 函數(shù)被調(diào)用,因此它被推到堆棧的頂部。setTimeout()有兩個參數(shù):

1) 回調(diào)和

2) 以毫秒(ms)為單位的時間。

setTimeout() 方法在web api環(huán)境中啟動一個2s的計時器。此時,setTimeout()已經(jīng)完成,并從堆棧中彈出。cosole.log(“the end”) 被推送到堆棧中,在完成后執(zhí)行并從堆棧中刪除。

同時,計時器已經(jīng)過期,現(xiàn)在回調(diào)被推送到消息隊列。但是回調(diào)不會立即執(zhí)行,這就是事件輪詢開始的地方。

事件輪詢

事件輪詢的工作是監(jiān)聽調(diào)用堆棧,并確定調(diào)用堆棧是否為空。如果調(diào)用堆棧是空的,它將檢查消息隊列,看看是否有任何掛起的回調(diào)等待執(zhí)行。

在這種情況下,消息隊列包含一個回調(diào),此時調(diào)用堆棧為空。因此,事件輪詢將回調(diào)推到堆棧的頂部。

然后是 console.log(“Async Code”) 被推送到堆棧頂部,執(zhí)行并從堆棧中彈出。此時,回調(diào)已經(jīng)完成,因此從堆棧中刪除它,程序最終完成。

消息隊列還包含來自DOM事件(如單擊事件和鍵盤事件)的回調(diào)。例如:

document.querySelector(".btn").addEventListener("click",(event) => {
  console.log("Button Clicked");
});

對于DOM事件,事件偵聽器位于web api環(huán)境中,等待某個事件(在本例中單擊event)發(fā)生,當該事件發(fā)生時,回調(diào)函數(shù)被放置在等待執(zhí)行的消息隊列中。

同樣,事件輪詢檢查調(diào)用堆棧是否為空,并在調(diào)用堆棧為空并執(zhí)行回調(diào)時將事件回調(diào)推送到堆棧。

延遲函數(shù)執(zhí)行

我們還可以使用setTimeout來延遲函數(shù)的執(zhí)行,直到堆棧清空為止。例如

const bar = () => {
  console.log("bar");
}
const baz = () => {
  console.log("baz");
}
const foo = () => {
  console.log("foo");
  setTimeout(bar, 0);
  baz();
}
foo();

打印結(jié)果:

foo
baz
bar

當這段代碼運行時,第一個函數(shù)foo()被調(diào)用,在foo內(nèi)部我們調(diào)用console.log("foo"),然后setTimeout()被調(diào)用,bar()作為回調(diào)函數(shù)和時0秒計時器。

現(xiàn)在,如果我們沒有使用 setTimeout, bar() 函數(shù)將立即執(zhí)行,但是使用 setTimeout 和0秒計時器,將bar的執(zhí)行延遲到堆棧為空的時候。

0秒后,bar()回調(diào)被放入等待執(zhí)行的消息隊列中。但是它只會在堆棧完全空的時候執(zhí)行,也就是在baz和foo函數(shù)完成之后。

ES6 任務(wù)隊列

我們已經(jīng)了解了異步回調(diào)和DOM事件是如何執(zhí)行的,它們使用消息隊列存儲等待執(zhí)行所有回調(diào)。

ES6引入了任務(wù)隊列的概念,任務(wù)隊列是 JavaScript 中的 promise 所使用的。消息隊列和任務(wù)隊列的區(qū)別在于,任務(wù)隊列的優(yōu)先級高于消息隊列,這意味著任務(wù)隊列中的promise 作業(yè)將在消息隊列中的回調(diào)之前執(zhí)行,例如:

const bar = () => {
  console.log("bar");
};

const baz = () => {
  console.log("baz");
};

const foo = () => {
  console.log("foo");
  setTimeout(bar, 0);
  new Promise((resolve, reject) => {
    resolve("Promise resolved");
  }).then(res => console.log(res))
    .catch(err => console.log(err));
  baz();
};

foo();

打印結(jié)果:

foo
baz
Promised resolved
bar

我們可以看到 promisesetTimeout 之前執(zhí)行,因為 promise 響應(yīng)存儲在任務(wù)隊列中,任務(wù)隊列的優(yōu)先級高于消息隊列。

小結(jié)

因此,我們了解了異步 JavaScript 是如何工作的,以及調(diào)用堆棧、事件循環(huán)、消息隊列和任務(wù)隊列等概念,這些概念共同構(gòu)成了 JavaScript 運行時環(huán)境。雖然成為一名出色的JavaScript開發(fā)人員并不需要學習所有這些概念,但是了解這些概念是有幫助的:)

參考:
Understanding Asynchronous JavaScript?—?the Event Loop

你的點贊是我持續(xù)分享好東西的動力,歡迎點贊!

歡迎加入前端大家庭,里面會經(jīng)常分享一些技術(shù)資源。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/99335.html

相關(guān)文章

  • JavaScript運行機制和事件循環(huán)

    摘要:主線程不斷重復上面的三步,此過程也就是常說的事件循環(huán)。所以主線程代碼執(zhí)行時間過長,會阻塞事件循環(huán)的執(zhí)行。參考資料這一次,徹底弄懂執(zhí)行機制任務(wù)隊列的順序機制事件循環(huán)搞懂異步事件輪詢與中的事件循環(huán) 1. 說明 讀過本文章后,您能知道: JavaScript代碼在瀏覽器中的執(zhí)行機制和事件循環(huán) 面試中經(jīng)常遇到的代碼輸出順序問題 首先通過一段代碼來驗證你是否了解代碼輸出順序,如果你不知道輸出...

    Ververica 評論0 收藏0
  • 徹底搞懂JavaScript執(zhí)行機制

    摘要:徹底搞懂執(zhí)行機制首先我們大家都了解的是,是一門單線程語言,所以我們就可以得出是按照語句順序執(zhí)行的首先看這個顯然大家都知道結(jié)果,依次輸出,然而換一種這個時候再看代碼的順序執(zhí)行,輸出,,,。不過即使主線程為空,也是達不到的,根據(jù)標準,最低是。 徹底搞懂JavaScript執(zhí)行機制 首先我們大家都了解的是,JavaScript 是一門單線程語言,所以我們就可以得出: JavaScript 是...

    hizengzeng 評論0 收藏0
  • 用一道大廠面試題帶你搞懂事件循環(huán)機制

    本文涵蓋 面試題的引入 對事件循環(huán)面試題執(zhí)行順序的一些疑問 通過面試題對微任務(wù)、事件循環(huán)、定時器等對深入理解 結(jié)論總結(jié) 面試題 面試題如下,大家可以先試著寫一下輸出結(jié)果,然后再看我下面的詳細講解,看看會不會有什么出入,如果把整個順序弄清楚 Node.js 的執(zhí)行順序應(yīng)該就沒問題了。 async function async1(){ console.log(async1 start) ...

    ShowerSun 評論0 收藏0
  • 深入前端-徹底搞懂JS的運行機制

    摘要:瀏覽器是多進程的詳情看我上篇總結(jié)瀏覽器執(zhí)行機制的文章深入前端徹底搞懂瀏覽器運行機制瀏覽器每打開一個標簽頁,就相當于創(chuàng)建了一個獨立的瀏覽器進程。執(zhí)行異步操作事件完成,回調(diào)函數(shù)進入。主線程從讀取回調(diào)函數(shù)并執(zhí)行。 最近看了很多關(guān)于JS運行機制的文章,每篇都獲益匪淺,但各有不同,所以在這里對這幾篇文章里說的很精辟的地方做一個總結(jié),參考文章鏈接見最后。本文博客地址 了解進程和線程 進程是應(yīng)用...

    luckyw 評論0 收藏0
  • 深入前端-徹底搞懂JS的運行機制

    摘要:瀏覽器是多進程的詳情看我上篇總結(jié)瀏覽器執(zhí)行機制的文章深入前端徹底搞懂瀏覽器運行機制瀏覽器每打開一個標簽頁,就相當于創(chuàng)建了一個獨立的瀏覽器進程。執(zhí)行異步操作事件完成,回調(diào)函數(shù)進入。主線程從讀取回調(diào)函數(shù)并執(zhí)行。 最近看了很多關(guān)于JS運行機制的文章,每篇都獲益匪淺,但各有不同,所以在這里對這幾篇文章里說的很精辟的地方做一個總結(jié),參考文章鏈接見最后。本文博客地址 了解進程和線程 進程是應(yīng)用...

    jaysun 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<