成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JavaScript 異步編程指南 — 關于協程的一些思考

開發 前端
在了解協程之前,先看進程、線程分別是什么,分享一個筆者之前寫的 Node.js 進階之進程與線程 文中結合 Node.js 列舉了一些示例,也是從一些基礎的層面來理解。

[[405491]]

本文轉載自微信公眾號「五月君」,作者五月君。轉載本文請聯系五月君公眾號。

從 Callback 到 Promise 的 .then().then()... 也是在不斷嘗試去解決異步編程帶來的回調嵌套、錯誤管理等問題,Promise 進一步解決了這些問題,但是當異步鏈多了之后你會發現代碼會變成這樣 .then().then()... 由原來的橫向變成了縱向的模式,仍就存在冗余的代碼,基于我們大腦對事物的思考,我們更傾向于一種近乎 “同步” 的寫法來表達我們的異步代碼,在 ES6 規范中為我們提供了 Generator 函數進一步改善我們的代碼編寫方式。

Generator 中文翻譯過來我們可以稱呼它為 “生成器”,它擁有函數的執行權,知道什么時候暫停、什么時候執行,這里還有一個概念協程,有些地方也看到過一些提問:“JavaScript 中有協程嗎?” “Node.js 中有協程嗎?” 這些問題正是本文討論的,本節著重從概念上讓大家做一些了解,認識到協程在 JavaScript 是怎么樣的存在。

進程 VS 線程 VS 協程?

在了解協程之前,先看進程、線程分別是什么,分享一個筆者之前寫的 Node.js 進階之進程與線程 文中結合 Node.js 列舉了一些示例,也是從一些基礎的層面來理解。

進程

進程(Process)是計算機中的程序關于某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操作系統結構的基礎,進程是線程的容器(來自百科)。

我們啟動一個服務、運行一個實例,就是開一個服務進程,例如 Java 里的 JVM 本身就是一個進程,Node.js 里通過 node app.js 開啟一個服務進程,多進程就是進程的復制(fork),fork 出來的每個進程都擁有自己的獨立空間地址、數據棧,一個進程無法訪問另外一個進程里定義的變量、數據結構,只有建立了 IPC 通信,進程之間才可數據共享。

Mac 系統自帶的監控工具 “活動監視器” 也可看到效果。

Node.js 中我們通過 Cluster 模塊創建多進程時為什么要根據 CPU 核心數?創建更多不好嗎?在一個 CPU 核心的任何時間內只能執行一個進程。因此,當你 CPU 核心數有限時,創建過多的進程,CPU 也是忙不過來的。

Node.js 通過單線程 + 事件循環解決了并發問題。而我們使用 Node.js 利用 Cluster 模塊根據 CPU 核心數創建多進程解決的是并行問題,假設我有 4 CPU 每個 CPU 分別對應一個線程并行處理 A、B、C、D 不同的任務,線程之間互不搶占資源。

一句話總結:進程之間數據完全隔離、由操作系統調度,自動切換上下文信息,屬系統層級的構造。

線程

線程是操作系統能夠進行運算調度的最小單位,首先我們要清楚線程是隸屬于進程的,被包含于進程之中。一個線程只能隸屬于一個進程,但是一個進程是可以擁有多個線程的。

同一塊代碼,可以根據系統 CPU 核心數啟動多個進程,每個進程都有屬于自己的獨立運行空間,進程之間是不相互影響的。同一進程中的多條線程將共享該進程中的全部系統資源,如虛擬地址空間,文件描述符和信號處理等。但同一進程中的多個線程有各自的調用棧(call stack),自己的寄存器環境(register context),自己的線程本地存儲(thread-local storage),線程又有單線程和多線程之分,具有代表性的 JavaScript、Java 語言。

線程共享進程的資源,可以由系統調度運行,可以自動完成線程切換,也許你會聽到多線程編程、并發問題,首先,并發指的某個時間點多個任務隊列對應到同一個 CPU 上運行,在任一時間點內也只會有一個任務隊列在 CPU 上執行,這時就產生排隊了。

為了解決這個問題,CPU 運行時間片會被分成多個 CPU 時間段,每個時間段給各個任務隊列執行(對應多個線程),這樣解決了一個任務如果造成阻塞,不會影響到其它的任務運行,同樣線程是會自動切換的。

Node.js 是怎么解決的并發問題?Node.js 主線程是單線程的,核心通過事件循環,每次循環時取出任務隊列中的可執行任務運行,沒有多線程上下文切換,資源搶占問題,達到高并發成就。

一句話總結:線程之間大多數共享數據(各自的調用棧這些信息除外),由操作系統調用,自動切換上下文,系統層級的構造。

協程

協程又稱為微線程、纖程,英文 Coroutine。協程類似于線程,但是協程是協作式多任務的,而線程是搶占式多任務的。協程之間的調用不需要涉及任何系統調用,是語言層級的構造,可看作一種形式的控制流,有時候我們也會稱它為用戶態的輕量級線程。

協程一個特點是通過關鍵字 yield 調用其它協程,接下來每次協程被調用時,從協程上次 yield 返回的位置接著執行,這種通過 yield 協作轉移執行權的操作,彼此沒有調用者和被調用者的關系,是彼此平等對稱的一種關系。

協程與線程兩者的差異,可以看出 “同一時間如果有多個線程,但它們會都處于運行狀態,線程是搶占式的,而協程同一時間運行的只有一個,其它的協程處于暫停狀態,執行權由協程自己分配”。

協程也不是萬能的,它需要配合異步 I/O 才能發揮最好的效果,對于操作系統而言是不知道協程的存在的,它只知道線程。需要注意,如果一個協程遇到了阻塞的 I/O 調用,這時會導致操作系統讓線程阻塞,那么在這個線程上的其它協程也都會陷入阻塞。

一句話總結:協程共享數據,由程序控制完成上下文切換,語言層級的構造。

JavaScript 有協程嗎

之前知乎上有個問題 “Node.js 真的有協程嗎?” 協程在很多語言中都支持,只是每個實現略有差異,下圖來自維基百科展示了支持協程的編程語言,可以看到 JavaScript 在 ECMAScript 6 支持,ECMAScript 7 之后通過 await 支持,Node.js 做為 JavaScript 在服務端的運行時,只要你的 Node.js 版本對應支持,就是可以的。

協程在 JavaScript 中的實現

生成器與協程

生成器(Generator)是協程的子集,也稱為 “半協程”。差異在于,生成器只能把控制權交給它的調用者,完全協程有能力控制在它讓位之后哪個協程立即接續它執行。在 JavaScript 里我們說的 Generator 函數就是 ES6 對協程的實現。

JavaScript 是一個單線程的語言,只能保持一個調用棧。在異步操作的回調函數里,一旦出錯原始的調用棧早已結束,引入協程之后每個任務可以保持自己的調用棧,這樣解決的一大問題是出錯誤時可以找到原始的調用棧。

看下生成器函數與普通函數有什么區別?首先普通函數通過棧實現的,舉個例子,調用時是 A() -> B() -> C() 入棧,最后是 C() -> B() -> A() 這樣一個順序最后進入的先出棧執行。

生成器函數看似和普通函數相似,其實內部執行機制是完全不同的,生成器函數在內部執行遇到 yield 會交出函數的執行權給其它協程(此處類似 CPU 中斷),轉而去執行別的任務,在將來一段時間后等到執行權返回(生成器還會把控制權交給它的調用者),程序再從暫停的地方繼續執行。

無堆棧協程

自 ES6 開始,通過 “Generator” 和 “yield” 表達式提供了無堆棧協程功能。

“無棧協程的秘密在于它們只能從頂級函數中掛起自己。對于其他所有函數,它們的數據都分配在被調用者堆棧上,因此從協程調用的所有函數必須在掛起協程之前完成。協程保留其狀態所需的所有數據都在堆上動態分配。這通常需要幾個局部變量和參數,其大小遠小于預先分配的整個堆棧”。參考 coroutines-introduction

棧是一塊連續的內存,能夠從子函數產生的協程稱為棧式,它們可以記住整個調用棧,這種也稱為棧式協程。在 JavaScript 中我們只能從生成器函數內部暫停、恢復執行生成器函數。

下面示例 test1() 是生成器函數,但是 forEach 里面的匿名函數是一個普通的函數,就無法在內部使用 yield 關鍵字,運行時會拋出錯誤 “SyntaxError: Unexpected identifier”

  1. function *test1() { 
  2.   console.log('execution start'); 
  3.    
  4.   ['A''B'].forEach(function(item) { 
  5.     yield item; 
  6.   }) 

生成器函數示例

例如,現在有兩個生成器函數 test1()、test2(),還有 co 這個工具可以幫助我們自動的執行生成器函數。

  1. const co = require('co'); 
  2. function *test1() { 
  3.   console.log('execution 1'); 
  4.   console.log(yield Promise.resolve(1)); 
  5.   console.log('execution 2'); 
  6.   console.log(yield Promise.resolve(2)); 
  7.  
  8. function *test2() { 
  9.   console.log('execution a'); 
  10.   console.log(yield Promise.resolve('a')); 
  11.   console.log('execution b'); 
  12.   console.log(yield Promise.resolve('b')); 
  13.  
  14. co(test1); 
  15. co(test2); 

看下運行結果:

  • 第一次程序執行 test1() 函數,先輸出 'execution 1' 遇到 yield 語句程序的控制權轉移。
  • 現在執行權轉移到了 test2() 函數,執行代碼輸出 'execution a' 當遇到 yield 語句后交出程序的控制權。
  • 此時 test1() 函數收回執行權,恢復執行輸出 '1' 繼續往下執行輸出 'execution 2' 當遇到 yield 語句再次交出執行權,依次類推。
  1. execution 1 
  2. execution a 
  3. execution 2 
  4. execution b 

總結

“JavaScript 有協程嗎?” JavaScript 中是在 ES6 后基于生成器函數(Generator)實現的,生成器只能把程序的執行權還給它的調用者,這種方式我們稱為 “半協程”,而完全的協程是任何函數都可讓暫停的協程執行。

基于生成器函數這種寫法,如果去掉 yield 關鍵字,與我們普通的函數是相似的,以一種同步的方式來表達,解決了回調嵌套的問題,另外我們還可以通過 try...catch 做錯誤捕獲,只不過我們還需要借助 CO 這樣的模塊,讓生成器函數自動執行,這個問題在 ES7 中已經得到了更好地解決,我們可以通過 async/await 輕松的實現。

Reference

https://en.wikipedia.org/wiki/Coroutine#Implementations_in_JavaScript

https://zhuanlan.zhihu.com/p/70256971

http://zhangchen915.com/index.php/archives/719/

https://es6.ruanyifeng.com/#docs/generator

 

責任編輯:武曉燕 來源: 五月君
相關推薦

2015-10-12 08:59:57

異步代碼測試

2017-08-10 15:50:44

PHP協程阻塞

2017-12-21 07:54:07

2021-06-10 10:02:19

優化緩存性能

2024-12-27 10:51:53

2012-12-19 09:36:49

測試自動化測試

2020-08-20 10:16:56

Golang錯誤處理數據

2009-08-27 11:02:22

JavaScript事

2021-09-16 09:59:13

PythonJavaScript代碼

2023-11-29 07:10:50

python協程異步編程

2009-06-25 09:50:32

JSF

2020-02-03 16:03:36

疫情思考

2021-08-08 10:44:33

安卓系統開發者手機廠商

2021-06-10 20:17:04

云網融合超融合

2018-06-29 14:51:41

Java健壯性實踐

2011-11-30 15:57:18

2015-03-30 11:21:27

編程編程反思

2011-01-19 10:50:31

軟件設計師

2011-07-13 09:13:56

Android設計
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99国内精品久久久久久久 | 亚洲啊v| 亚洲人成一区二区三区性色 | 日韩精品视频在线观看一区二区三区 | 国产精品久久久久久久久久免费看 | 精品一区二区三区在线视频 | 国产极品粉嫩美女呻吟在线看人 | 久久久精品国产 | 91偷拍精品一区二区三区 | 日韩高清电影 | 国产精品亚洲二区 | 懂色一区二区三区免费观看 | 精品91久久久 | 一区二区三区四区在线视频 | 成人三级在线播放 | 欧美激情一区二区 | 日本一区精品 | 亚洲视频二区 | 国产午夜精品久久久久免费视高清 | 色爱综合 | 九九热这里只有精品在线观看 | 国产精品一区二区三区四区五区 | 毛片黄| 亚洲手机视频在线 | 羞羞的视频在线观看 | 亚洲成人在线视频播放 | 国产9 9在线 | 中文 | 美人の美乳で授乳プレイ | 国产精品特级毛片一区二区三区 | 亚洲欧美中文字幕 | 亚洲精品一二三 | 黑人精品 | 免费一看一级毛片 | 91在线免费视频 | 午夜精品久久久久久久久久久久久 | 日本欧美国产 | 久久精品视频一区二区 | 99精品国产一区二区三区 | 美女视频一区 | 超碰最新在线 | 欧美日韩午夜精品 |