主頁 > 前端設計 > JavaScript為什么是單執行緒-JS異步與回呼詳解

JavaScript為什么是單執行緒-JS異步與回呼詳解

2021-08-13 07:47:32 前端設計

JavaScript為什么是單執行緒

JavaScript 最初被設計為瀏覽器腳本語言,主要用途包括對頁面的操作、與瀏覽器的互動、與用戶的互動、頁面邏輯處理等,如果將 JavaScript 設計為多執行緒,那當多個執行緒同時對同一個 DOM 節點進行操作時,執行緒間的同步問題會變得很復雜,

同步任務與異步任務

  • 同步任務:在主執行緒上排隊執行的任務,前一個任務完整地執行完成后,后一個任務才會被執行,

  • 異步任務:不會阻塞主執行緒,在其任務執行完成之后,會再根據一定的規則去執行相關的回呼,

同步任務與函式呼叫堆疊

在 JavaScript 中,同步任務基本上可以認為是執行 JavaScript 代碼,在上一講內容中,我們提到 JavaScript 在執行程序中每進入一個不同的運行環境時,都會創建一個相應的執行背景關系,那么,當我們執行一段 JavaScript 代碼時,通常會創建多個執行背景關系,

而 JavaScript 解釋器會以堆疊的方式管理這些執行背景關系、以及函式之間的呼叫關系,形成函式呼叫堆疊(call stack)(呼叫堆疊可理解為一個存盤函式呼叫的堆疊結構,遵循 FILO(先進后出)的原則),

我們來看一下 JavaScript 中代碼執行的程序:

  1. 首先進入全域環境,全域執行背景關系被創建并添加進堆疊中;

  2. 每呼叫一個函式,該函式執行背景關系會被添加進呼叫堆疊,并開始執行;

  3. 如果正在呼叫堆疊中執行的 A 函式還呼叫了 B 函式,那么 B 函式也將會被添加進呼叫堆疊;

  4. 一旦 B 函式被呼叫,便會立即執行;

  5. 當前函式執行完畢后,JavaScript 解釋器將其清出呼叫堆疊,繼續執行當前執行環境下的剩余的代碼,

由此可見,JavaScript 代碼執行程序中,函式呼叫堆疊堆疊底永遠是全域執行背景關系,堆疊頂永遠是當前執行背景關系

在不考慮全域執行背景關系時,我們可以理解為剛開始的時候呼叫堆疊是空的,每當有函式被呼叫,相應的執行背景關系都會被添加到呼叫堆疊中,執行完函式中相關代碼后,該執行背景關系又會自動被呼叫堆疊移除,最后呼叫堆疊又回到了空的狀態(同樣不考慮全域執行背景關系),

呼叫堆疊示例

還是來個示例,捋清楚一下

let a = 10;
function test() {
    console.log("你好");
};
test();

通過Chrome的除錯工具可以看到,代碼執行程序中產生了兩個執行背景關系,當前堆疊頂為test函式的執行背景關系,順便一說,這里的Scope中有三個:LocalScriptGlobalLocal代表函式的作用域,而Script則是let關鍵字產生的塊級作用域,Global毋庸置疑就是全域環境

image-20210810091552016

我們也可以直接通過console.trace();API向控制臺輸出一個輸出一個堆疊跟蹤

image-20210810092049409

但是堆疊的容量是有限制的,所以當我們沒有合理呼叫函式的時候,可能會導致爆堆疊例外,此時控制臺便會拋出錯誤:

遞回爆堆疊

function fn(n) {
    if (n < 1) {
        return
    }
    console.log(n);
    fn(n - 1)
}

fn(100000)

image-20210810092608156

這樣的一個函式呼叫堆疊結構,可以理解為 JavaScript 中同步任務的執行環境,同步任務也可以理解為 JavaScript 代碼片段的執行,

同步任務的執行會阻塞主執行緒,也就是說,一個函式執行的時候不會被搶占,只有在它執行完畢之后,才會去執行任何其他的代碼,這意味著如果我們一個任務執行的時間過長,瀏覽器就無法處理與用戶的互動,例如點擊或滾動

因此,我們還需要用到異步任務,

異步任務與回呼佇列

異步任務

異步任務包括一些需要等待回應的任務,包括用戶互動、HTTP 請求、定時器等,

setTimeout(() => {
    console.log("張三")
}, 0);
console.log("你好");

image-20210810093148315

此處可以看出,同步任務總是優先于異步任務執行,即使定時器定時為0,但其實不是真正的0,

我們知道,I/O 型別的任務會有較長的等待時間,對于這類無法立刻得到結果的事件,可以使用異步任務的方式,這個程序中 JavaScript 執行緒就不用處于等待狀態,CPU 也可以處理其他任務,

異步任務需要提供回呼函式,當異步任務有了運行結果之后,該任務則會被添加到回呼佇列中,主執行緒在適當的時候會從回呼佇列中取出相應的回呼函式并執行,

回呼佇列

這里提到的回呼佇列又是什么呢?

實際上,JavaScript 在運行的時候,除了函式呼叫堆疊之外,還包含了一個待處理的回呼佇列,在回呼佇列中的都是已經有了運行結果的異步任務,每一個異步任務都會關聯著一個回呼函式,

回呼佇列則遵循 FIFO(先進先出)的原則,JavaScript 執行代碼程序中,會進行以下的處理:

  1. 運行時,會從最先進入佇列的任務開始,處理佇列中的任務;

  2. 被處理的任務會被移出佇列,該任務的運行結果會作為輸入引數,并呼叫與之關聯的函式,此時會產生一個函式呼叫堆疊;

  3. 函式會一直處理到呼叫堆疊再次為空,然后 Event Loop 將會處理佇列中的下一個任務,

這里我們提到了 Event Loop,它主要是用來管理單執行緒的 JavaScript 中同步任務和異步任務的執行問題,

示例

此處還是舉一個栗子吧

window.setTimeout除了回呼函式和延遲時間外,還有一個可選的引數,當定時器到期,會作為引數傳遞給回呼函式

setTimeout((name) => {
    console.log(name)
    console.trace();
}, 0, "張三");
console.log("你好");

image-20210810201152298

可以看出,的的確確是將結果傳遞過去了,并且產生了相應的呼叫堆疊

單執行緒的 JavaScript 是如何管理任務的

事件回圈Event Loop

我們知道,單執行緒的設計會存在阻塞問題,為此 JavaScript 中任務被分為同步和異步任務,那么,同步任務和異步任務之間是按照什么順序來執行的呢?

JavaScript 有一個基于事件回圈的并發模型,稱為事件回圈(Event Loop),它的設計解決了同步任務和異步任務的管理問題,

根據 JavaScript 運行環境的不同,Event Loop 也會被分成瀏覽器的 Event Loop 和 Node.js 中的 Event Loop,

瀏覽器的 Event Loop

在瀏覽器里,每當一個被監聽的事件發生時,事件監聽器系結的相關任務就會被添加進回呼佇列,通過事件產生的任務是異步任務,常見的事件任務包括:

  • 用戶互動事件產生的事件任務,比如輸入操作;

  • 計時器產生的事件任務,比如setTimeout

  • 異步請求產生的事件任務,比如 HTTP 請求,

JavaScript 的運行程序,可以借用 Philip Roberts 演講《Help, I’m stuck in an event-loop》中經典的一張圖來描述:

event-loop

如圖,主執行緒運行的時候,會產生堆(heap)和堆疊(stack),其中堆為記憶體、堆疊為函式呼叫堆疊,我們能看到,Event Loop 負責執行代碼、收集和處理事件以及執行佇列中的子任務,具體包括以下程序,

這個程序很重要,理解記憶

  1. JavaScript 有一個主執行緒和呼叫堆疊,所有的任務最終都會被放到呼叫堆疊等待主執行緒執行,

  2. 同步任務會被放在呼叫堆疊中,按照順序等待主執行緒依次執行,

  3. 主執行緒之外存在一個回呼佇列,回呼佇列中的異步任務最侄訓在主執行緒中以呼叫堆疊的方式運行,

  4. 同步任務都在主執行緒上執行,堆疊中代碼在執行的時候會呼叫瀏覽器的 API,此時會產生一些異步任務,

  5. 異步任務會在有了結果(比如被監聽的事件發生時)后,將異步任務以及關聯的回呼函式放入回呼佇列中,

  6. 呼叫堆疊中任務執行完畢后,此時主執行緒處于空閑狀態,會從回呼佇列中獲取任務進行處理,

上述程序會不斷重復,這就是 JavaScript 的運行機制,稱為事件回圈機制(Event Loop),

Event Loop 的設計會帶來一些問題,比如setTimeoutsetInterval的時間精確性,這兩個方法會設定一個計時器,當計時器計時完成,需要執行回呼函式,此時才把回呼函式放入回呼佇列中,

如果當回呼函式放入佇列時,假設佇列中還有大量的回呼函式在等待執行,此時就會造成任務執行時間不精確,

要優化這個問題,可以使用系統時鐘來補償計時器的不準確性,從而提升精確度,舉個例子,如果你的計時器會在回呼時觸發二次計時,可以在每次回呼任務結束的時候,根據最初的系統時間和該任務的執行時間進行差值比較,來修正后續的計時器時間,

Node.js 中的 Event Loop

除了瀏覽器,Node.js 中同樣存在 Event Loop,由于 JavaScript 是單執行緒的,Event Loop 的設計使 Node.js 可以通過將操作轉移到系統內核中,來執行非阻塞 I/O 操作,

Node.js 中的事件回圈執行程序為:

  1. 當 Node.js 啟動時將初始化事件回圈,處理提供的輸入腳本;

  2. 提供的輸入腳本可以進行異步 API 呼叫,然后開始處理事件回圈;

  3. 在事件回圈的每次運行之間,Node.js 會檢查它是否正在等待任何異步 I/O 或計時器,如果沒有,則將其干凈地關閉,

與瀏覽器不一樣,Node.js 中事件回圈分成不同的階段:

   ┌───────────────────────────┐

┌─>│           timers          │

│  └─────────────┬─────────────┘

│  ┌─────────────┴─────────────┐

│  │     pending callbacks     │

│  └─────────────┬─────────────┘

│  ┌─────────────┴─────────────┐

│  │       idle, prepare       │

│  └─────────────┬─────────────┘      ┌───────────────┐

│  ┌─────────────┴─────────────┐      │   incoming:   │

│  │           poll            │<─────┤               |

│  └─────────────┬─────────────┘      │   data, etc.  │

│  ┌─────────────┴─────────────┐      └───────────────┘

│  │           check           │

│  └─────────────┬─────────────┘

│  ┌─────────────┴─────────────┐

└──┤      close callbacks      │

   └───────────────────────────┘

由于事件回圈階段劃分不一致,Node.js 和瀏覽器在對宏任務和微任務的處理上也不一樣,

Event Loop描述
timers此階段由setTimeout()和安排的回呼setInterval()執行
pending callbacks執行推遲到下一個回圈迭代的I/O回呼
idle/prepare僅在Node.js內部使用
poll檢索新的I/O事件,執行與I/O相關的回呼,節點將在此處阻塞
checksetImmediate()在這里回呼
close callbacks一些關倍訓呼,例如socket.on('close',...)

宏任務和微任務

事件回圈中的異步回呼佇列有兩種:宏任務(MacroTask)和微任務(MicroTask)佇列,

什么是宏任務和微任務呢?

  • 宏任務:包括 script 全部代碼、setTimeoutsetIntervalsetImmediate(Node.js)、requestAnimationFrame(瀏覽器)、I/O 操作、UI 渲染(瀏覽器),這些代碼執行便是宏任務,
  • 微任務:包括process.nextTick(Node.js)、PromiseMutationObserver,這些代碼執行便是微任務,

區分的目的

為什么要將異步任務分為宏任務和微任務呢?這是為了避免回呼佇列中等待執行的異步任務(宏任務)過多,導致某些異步任務(微任務)的等待時間過長,在每個宏任務執行完成之后,會先將微任務佇列中的任務執行完畢,再執行下一個宏任務,

因此,前面我們所說的回呼佇列可以理解為宏任務佇列,同時還有另外一個任務佇列為微任務佇列,

宏任務和微任務的執行程序

在瀏覽器的異步回呼佇列中,宏任務和微任務的執行程序如下:

  1. 0宏任務佇列一次只從佇列中取一個任務執行,執行完后就去執行微任務佇列中的任務,
  2. 微任務佇列中所有的任務都會被依次取出來執行,直到微任務佇列為空,
  3. 在執行完所有的微任務之后,執行下一個宏任務之前,瀏覽器會執行 UI 渲染操作、更新界面,

我們能看到,在瀏覽器中每個宏任務執行完成后,會執行微任務佇列中的任務,而在 Node.js 中,事件回圈分為 6 個階段,微任務會在事件回圈的各個階段之間執行,也就是說,每當一個階段執行完畢,就會去執行微任務佇列的任務,

宏任務和微任務的執行順序,常常會被用作面試題,比如下面這道考察PromisesetTimeoutasync/await等 API 執行順序的題目:

console.log("script start");

setTimeout(() => {
  console.log("setTimeout");
}, 1000);

Promise.resolve()

  .then(function () {
    console.log("promise1");
  })

  .then(function () {
    console.log("promise2");
  });

async function errorFunc() {
  try {
    await Promise.reject("error!!!");
  } catch (e) {
    console.log("error caught"); // 微1-3
  }

  console.log("errorFunc");

  return Promise.resolve("errorFunc success");
}

errorFunc().then((res) => console.log("errorFunc then res"));

console.log("script end");

分析

首先執行同步代碼

console.log("script start");
setTimeout(() => {
    // 8
  console.log("setTimeout");
}, 1000);
console.log("script end");

但由于setTimeout是異步任務,所以不會等待它執行完成,

而是開啟一個瀏覽器的定時器執行緒,這個執行緒在渲染器行程中,指定一個回呼函式,到期后再講回呼函式放入異步回呼佇列

執行完以上代碼后,此時的微任務有 Promise.resolve(), errorFunc(),把它們加入異步回呼佇列

此時主執行緒是空閑的,就開始從異步回呼佇列開始提前任務,回呼佇列是先進先出,所以最先取出的是Promise.resolve(),將它調入呼叫堆疊,接著主執行緒就從呼叫堆疊中取出它運行,此時輸出promise1,此時.then()會產生一個微任務,將其加入到異步回呼佇列中

接下來從回呼佇列中調出errorFunc()

async function errorFunc() {
  try {
    await Promise.reject("error!!!");
  } catch (e) {
    console.log("error caught");
  }
  console.log("errorFunc");
  return Promise.resolve("errorFunc success");
}

因為 await會阻塞異步操作, 所以這個 await后面的 Promise不會進入回呼佇列排隊, 而是等待完成,捕獲到錯誤,此時輸出error caught,接著是同步代碼,直接輸出errorFunc,然后回傳的promise物件也會被加入到異步回呼佇列中

接著從回呼佇列中取出第一次.then()產生的微任務,執行第二個.then(),輸出promise2

接著執行

errorFunc().then((res) => console.log("errorFunc then res"));

輸出errorFunc then res

最后setTimeout的等待時間到了,將其回呼函式加入回呼佇列,并執行,輸出setTimeout

注意:因為宏任務佇列一次取一個任務執行,執行完成后執行所有的微任務,當所有的微任務執行完成后在執行下一個宏任務,所以就算等待時間為0,也是在最后執行

所以最終結果為

"script start", 
"script end",
"promise 1",
"error caught",
"errorFunc", 
"promise 2",
"errorFunc then res", 
"setTimeout"

補充

Promise.resolve()
  .then(function () {
    console.log("promise1");
  })

以上代碼會回傳一個成功態的promise物件,也就是fulfilled狀態

小結

介紹了 JavaScript 的單執行緒設計,它的設計初衷是為了讓用戶獲得更好的互動體驗,同時,為了避免單執行緒的任務執行程序中發生阻塞,事件回圈(Event Loop)機制便出現了,

在瀏覽器和 Node.js 中,都存在單執行緒的 Event Loop 設計,它們之間的不一致主要表現為 Event Loop 階段劃分以及宏任務和微任務的處理,

或許你會感到疑惑,除了應對面試以外,掌握 JavaScript 的事件回圈、宏任務和微任務相關機制,對我們有什么用處呢?

要知道,瀏覽器中在執行 JavaScript 代碼的時候不會進行頁面渲染,如果一項任務花費的時間太長,瀏覽器將無法執行其他任務(例如處理用戶事件),因此,當存在大量復雜的計算、或導致了死回圈的編程錯誤時,甚至會使頁面終止,

我們可以更合理地利用這些機制來拆分任務,比如考慮將多次觸發的資料變更通過微任務收集起來,再一起進行 UI 的更新和渲染,便可以降低瀏覽器渲染的頻率,提升瀏覽器的性能,給到用戶更好的體驗,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/293373.html

標籤:其他

上一篇:Node.js 模塊以及npm包的管理和使用

下一篇:深入理解JavaScript原型與閉包

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more