JS中的同步/異步編程

1. 進程(process)/線程(thread)

進程process: 電腦端安裝很多的應用軟件,每當運行一個應用程序,相當於開闢一個進程(而對於瀏覽器來說,每新建一個頁卡訪問一個頁面,都是新開闢一個進程)

  • 任務管理器可以查看進程

線程thread: 每一個進程中可能還會同時做很多事情,如果程序中需要同時處理很多事情,則需要開闢多個線程(一個線程同時只能做一個事情)

=> 一個進程中,會包含0~多個線程

理解:

  • 每一個飯店是一個進程
  • 飯店裏面每一個點餐的服務員就是一個線程

2. JS是單線程的線程

瀏覽器是‘多線程’的,但是JS渲染或者頁面渲染是‘單線程’的

2.1 瀏覽器中線程的分類

  • GUI渲染線程(渲染和繪製頁面)
  • JS引擎線程(運行和渲染JS代碼)
  • 事件管控和觸發線程
  • 定時器管控和觸發線程
  • 異步HTTP請求線程
  • ...

2.2 JS代碼的渲染是單線程的

瀏覽器是多線程的(打開一個頁面以後,瀏覽器至少要分配好幾個線程同時去處理事情),但是瀏覽器只會分配出一個線程去渲染代碼(GUI渲染線程),所以說JS是單線程的:‘在JS代碼執行過程中,一次只能處理一個事情’

3. 同步與異步

  • 同步編程【單線程】:任務是一次執行,上面的任務沒有執行完成,下面的任務是不能去操作的
  • 異步編程【多線程】:同時可以處理很多事情,但是JS中的異步編程是利用瀏覽器的相關機制構造出來的異步效果

通俗的理解:比如我們在食堂打飯這個場景: 我們在排隊打飯,但是當排到B打飯的時候,他的王者榮耀遊戲還沒有打完(吃飯不積極,思想...😅)

  • 此時同步編程思想處理的方式就是:我們大家都等B打完遊戲,只要B不打飯,後面的也不能打飯(B不會就是食堂老闆的兒子吧,這麼多人等他打遊戲🤔)
  • 但是異步編程思想處理方式是這樣的:不管你的後臺有多大,騰不出手,就去旁邊的任務隊列等着去,等我們大家都打完飯,你的遊戲也打完了,再來打飯(這你還怎麼猖狂😂)
同步編程
同步編程
異步編程
異步編程

3.1 常見的異步代碼

  • 定時器:設置定時器是同步(立即設置),異步指的是間隔多久後執行指定的函數
  • 事件綁定(監聽)
  • AJAX的異步請求
  • promise/async/await

4 定時器

  • setTimeout
  • setInterval

4.1 定時器的返回值

返回值:是一個數字,代表當前是第幾個定時器

  • 我們後期可以基於clearTimeout / clearInterval 清除定時器
  • 手動把timer賦值爲null,後期基於它的值驗證是否存在定時器

4.2 瀏覽器的最小反應時間

  • 定時器是異步的:遇到定時器先不執行,先去執行其他事情,等到所有事情做完再看那個定時器到達時間然後可以立馬執行;
  • 定時器的等待時間即使設置爲零,也不是立即執行,瀏覽器有一個最小的等待時間(谷歌5~6MS IE瀏覽器10~13MS
  • 最小反應時間:在滾動相同的距離下,事件被觸發多少次,取決於滾動所用的時間(速度)來決定的,瀏覽器有最小的反應時間,假設是5MS,整體運動時間100MS,這段時間內,瀏覽器能夠識別出來的次數是100/5 = 20次。同理,如果我們運動1000MS,那麼識別觸發的次數就是1000/5=200次

4.3 從定時器的執行過程理解它的異步的

  • 把代碼拿到棧中執行,當遇到異步代碼定時器的時候,會立即把定時器拿到任務隊列中去等待一定時間
  • 當把主線程的所有代碼執行完畢之後,去任務隊列中查看哪個定時器到達時間,把到達時間的定時器拿到棧中執行(這種操作是異步)

=> 遇到定時器不是不處理,而是把它放在任務隊列,等到主線程空閒下來,再去任務隊列查看,這種操作是異步。中途定時器到達時間了,但是主線程並沒有到達時間,此時也不會立即執行定時器,必須等到主線程空閒下來。

4.4 下面是關於定時器的對異步代碼的理解,配有圖片和註釋的說明👇

4.4.1 題目一

let n = 0;
setTimeout(() => {
   n++;
   console.log(n);   //=> 3 (2)
}, 1000);  //一秒鐘之後執行
n += 2;
console.log(n);    //=> 2 (1)
題目一
題目一

4.4.2 題目二

let n = 0;
setTimeout(() => {
   n++;
   console.log(n);   //=> 3 (2)
},0);  //寫零也不是立即執行,而是有一個最小的等待時間:10ms左右
n += 2;
console.log(n);   //=> 2 (1)

4.4.3 題目三

let n = 10;
setTimeout(() => {
   n++;
   console.log(n);    //=>11(3)
}, 0);        
console.log(n);  //=>10(1)
for(let i = 0;i < 99999999; i++){}
console.log(n);  //=>10(2)
題目三
題目三

4.4.4 題目四

time / timeEnd:獲取他們中間代碼執行所需要的時間(這個時間需要受到電腦配置、和當前電腦運行的環境等多方面因素影響),時間只作爲參考

setTimeout(() => {
    console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
    console.log(3);
}, 10);
console.log(4);
console.time('AA');
for (let i = 0; i < 90000000; i++) {
    // do soming
}
console.timeEnd('AA'); //=>AA: 79ms 左右
console.log(5);
setTimeout(() => {
    console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
    console.log(8);
}, 15);
console.log(9);
題目四
題目四

4.4.5 題目五

console.log(1); //=>1(1)
setTimeout(function () {
 console.log(2);
}, 20);
console.log(3); //=>3(2)
for (let i = 0; i > -1; i++) {} //=>死循環,GUI線程啥都做不了,一直在這加載(其它什麼事情都幹不了)
console.log(4);
setTimeout(function () {
 console.log(5);
}, 10);
console.log(6);

5. 事件循環 Event Loop

定義:JS是單線程的,因爲瀏覽器只分配一個線程自上而下加載代碼。所以JS中大部分任務都是同步任務。但是一定也有異步任務,定時器、事件綁定等這些都屬於異步任務。

而瀏覽器處理JS中的異步任務是:在JS代碼自上而下執行的時候,代碼進棧執行,執行完出棧,在這反反覆覆進行的過程中。當遇到定時器等異步任務的時候,會把當前任務放在等待任務隊列(Event Queue)中存起來,並且存起來之後不會影響下面代碼的執行,主線程會繼續執行。當 把下面的同步任務執行完成之後,主線程空閒下來了會去等待隊列找哪一個任務到達指定的時間點,就拿到主線程中去執行。執行完之後再去等待隊列中查看...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章