Javascript 異步編程(一)初識異步

關于同步和異步,我們先來看兩個例子。

const async=()=>{
  console.log('async..',1)
  let t=new Date();
  while (true){
    if(+new Date()-t>=2000){
      console.log('async...',2)
      break
    }
  }
  console.log('async...',3)
}
async();
//async.. 1
//async... 2
//async... 3

順序執行

const sync=()=>{
  console.log('sync...',1);
  setTimeout(()=>{
    console.log('sync...',2)
  },2000);
  console.log('sync...',3)
}
sync();
//sync... 1
//sync... 3
//sync... 2

可能都知道JavaScript是單線程的,即同一時刻只能做一件事,如果有多個任務,則需要排隊執行,但是這樣同步執行的效率低,如果一個任務長時間據有CPU,其他任務則需要等待,這無疑會浪費資源,造成資源利用率低。為此,
JavaScript將任務的執行分為兩種:

  • 同步:后一個任務等待前一個任務結束,然后再執行,程序的執行順序與任務的排列順序是一致的、同步的;
  • 異步:后一個任務不等前一個任務結束就執行,程序的執行順序與任務的排列順序是不一致的、異步的

需要了解的基本知識

進程與線程

進程(process):我們知道,程序運行是需要系統資源的(CPU,內存,I/O等)為了能使程序能夠并發執行,并對并發執行的程序加以描述和控制,從而引入進程。是進程實體的運行過程,是系統進行資源分配和調動的獨立單位

線程(thread):通過引入線程,一個能獨立運行的基本單位,作為操作系統調度和分派的基本單位。通過減少程序在并發進行時所付出的時空開銷,從而提高程序并發執行的程度,以及提高資源利用率和系統的吞吐量

進程和線程的區別和關系:

  • 進程是操作系統分配資源的最小單位,線程是程序執行的最小單位。
  • 一個進程由一個或多個線程組成,線程是一個進程中代碼的不同執行路線;
  • 進程之間相互獨立,但同一進程下的各個線程之間共享程序的內存空間(包括代碼段、數據集、堆等)及一些進程級的資源(如打開文件和信號)。
  • 調度和切換:線程上下文切換比進程上下文切換要快得多。

鏈接

以工廠模式比喻,江南皮革廠老板苦惱產量提不上去,于是給車間主任(進程)開會。
老板:你們幾個怎么搞得,怎么訂單(多任務)多了,產量還跟不上了?
車間主任A:手底下這么多人,哪管的過來。訂單不好分配、進度不無法跟蹤
車間主任B:A車間經常跟我們車間搶物料(系統資源)
車間主任A:放***屁,你們的人上個月還搶我設備呢(系統資源)
老板:當然你們說的都是客觀存在的因素,你們不會從手下挑選幾個有能力的人成立班組(線程)么?權利下放(獨立調度)
車間主任A,B:好好好,試試。

linux下查看進程的常用命令

  • ps
  • top

Javascript 單線程

ecma-262中并無與線程相關的內容,其單線程/多線程主要依賴于其JavaScript引擎(解釋器)。

瀏覽器的進程和線程

以Chrome為例,Chrome瀏覽器使用多個進程來隔離不同的網頁。因此在Chrome中打開一個網頁相當于起了一個進程


image_2.png

多進程的原因:

  1. 因為進程之間相互獨立,一個瀏覽器選項卡無響應不會影響其他的選項卡。
  2. 同樣因為進程之間相互獨立,一個選項卡中如果惡意腳本,不會影響其他選項卡。例如,Chrome 瀏覽器可以對處理用戶輸入(如渲染器)的進程,限制其文件訪問的權限。

GUI線程:渲染布局(HTML,CSS等)

JS引擎線程:1.解析、執行JS 2.與GUI線程互斥 (因為引擎是單線程的,所以Javascript是單線程的)

定時觸發器線程:setTimeout/setInterval

事件觸發線程:將滿足觸發條件的時間放入任務隊列

異步HTTP請求線程:XHR所在線程

單線程的原因(引用自瀏覽器進程?線程?傻傻分不清楚!
這是因為Javascript這門腳本語言誕生的使命所致:JavaScript為處理頁面中用戶的交互,以及操作DOM樹、CSS樣式樹來給用戶呈現一份動態而豐富的交互體驗和服務器邏輯的交互處理。如果JavaScript是多線程的方式來操作這些UI DOM,則可能出現UI操作的沖突; 如果Javascript是多線程的話,在多線程的交互下,處于UI中的DOM節點就可能成為一個臨界資源,假設存在兩個線程同時操作一個DOM,一個負責修改一個負責刪除,那么這個時候就需要瀏覽器來裁決如何生效哪個線程的執行結果。當然我們可以通過鎖來解決上面的問題。但為了避免因為引入了鎖而帶來更大的復雜性,Javascript在最初就選擇了單線程執行。

Node.js中的進程和線程

當一個 Node.js 的應用啟動的同時,它會啟動如下模塊:

  • 一個進程
  • 一個線程:單線程意味著在當前進程中同一時刻只有一個指令在執行。
  • 事件循環機制:盡管 JavaScript 是單線程的,但通過使用回調,promises, async/await 等語法,基于事件循環將對操作系統的操作異步化,使得 Node 擁有異步非阻塞 IO 的特性。
  • JS 引擎實例
  • Node.js 實例

Node 運行在單線程上,并且在事件循環中同一時刻只有一個進程的任務被執行,每次同一時刻只會執行一段代碼(多段代碼不會同時執行)。
只有一個js引擎在主線程上運行。其他異步IO和事件驅動相關的線程通過libuv來實現內部的線程池和線程調度

為什么Node.js也是單線程呢?

因為JavaScript起初是運行在瀏覽器端的...你懂的。

Node.js如何實現并行:

它通過事件輪詢(event loop)來實現并行操作,因此我們要避免阻塞操作,

Node.js可以多進程/多線程么?

image_8.png

前端的異步場景

  1. 定時器
  2. 網絡請求
  3. 事件綁定
  4. ES6 Promise

定時器

再回到我們的代碼

 console.log('sync...',1);
  setTimeout(()=>{
    console.log('sync...',2)
  },2000);
  console.log('sync...',3)

流程分析:

  1. 主程序調用棧
  2. 調用webAPI-setTimeout
  3. 定時器線程計數2s
  4. 事件觸發線程將定時器時間放入任務隊列
  5. 主線程通過Event Loop遍歷任務隊列執行異步任務 console.log()
console.time('test')
const test=()=>{
  let t=+new Date();
  while (true){
    if(+new Date()-t>=5000){
      break;
    }
  }
  setTimeout(()=>{
    console.log('setTimeout...')
  },2000)
}
test();
console.timeEnd('test');

注意:

  1. 定時任務可能不會按時執行,延遲原因(等待同步任務、等待CPU加載)
  2. 定時器嵌套5次之后最小間隔不能低于4ms

應用場景:

  1. 防抖
  2. 節流
  3. 倒計時
  4. 動畫(存在丟幀)

案例分析

for(var i=1;i<=10;i++){
  setTimeout(function() {
    console.log(i)
  },1000*i)
}

在這個案例中,我們期望每隔1s,依次打印i的值。但實際結果卻是每隔1s重復打印11

問題的原因在于:

  1. 定時器需要等待同步任務執行完成,那么i的值為11。
  2. var是全局作用域

修復方案:

//使用閉包,保留當前的作用域
for(var i=1;i<=10;i++){
  (i=>{
    setTimeout(function() {
      console.log(i)
    },1000*i)
  })(i)
}
//使用let 保留當前的作用域
for(let i=1;i<=10;i++){
  setTimeout(function() {
    console.log(i)
  },1000*i)
}

待學習

當我們談論 cluster 時我們在談論什么(上)

Referecnes

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容