Stream 的基礎

零、參考資料

一、對概念的理解

"流"(stream)是一個很抽象的概念,《C程序設計語言》中這樣定義:流與磁盤或其它外圍設備關聯的數據的源或目的地。 - 一樣的費解。
從字面意義上,我們能找到的最貼合的是"水流",因此,通俗意義上就可以解釋成像水流一樣長長的一串的東西。
物質意義上的水流,是作爲內容的水+作爲外部的容器+時間共同形成,而在編程語言中的流,並不嚴格採用這三樣基本元素。
編程語言中,流是對數據的一種描述,是一個視圖,是一個對象,因此,很可能是先有一個視圖(殼,空的容器),其次才與數據(水)關聯,最後在時間的作用下數據逐步進入視圖中,形成編程意義上的"流"
不過流不是容器,其內涵遠不止容器,或者說,容器可以被視作一個流,以流的形式進行讀寫操作。

二、爲什麼會有流

流的概念挺抽象的,這個概念的產生自然有其應用場景。所以在 JavaScript 中,流是爲何而產生?
其實很簡單,數據從一個媒介進入到另外一個媒介是有成本的。我們都知道在計算機架構中,cpu 運算速度最快,但是不存儲數據,硬盤存儲數據,但是運算速度慢,所以這就有了時間差,即使在目前的架構中有內存、緩存等的設計,但是這個矛盾始終存在。而網絡與計算機 cpu 的速度差就更明顯了,總不能一直把 cpu 鎖着,等網絡數據全部加載完成再去進行運算。所以流就是在這樣的需求下產生了。說白了,就是數據來一點,讓 cpu 處理一點,數據沒來,釋放 cpu,讓 cpu 去處理其他程序

三、js 中與流相關的術語和概念

(一)可讀流

一個可讀流是一個數據源,在 JavaScript 中用一個 ReadableStream 對象表示,數據從它的底層源(underlying source)流出——底層源表示一個你希望從中獲取數據,且來自網絡或其他域的某種資源(比如某些硬件的輸出流)

  • chunk - 分塊:FE 打包工具中也有這個概念,將各個組件分成獨立單元,打包成獨立文件,這樣在運行時環境中可以按需加載。對於數據,也是類似的概念。數據(字節、類型化數組)被按序讀入(分割成)很多小的片段,這些小的片段就是 chunk
  • enqueued - 入隊:講這些分塊有序放入流中,這個操作就是入隊 —— 這意味着它們已經在隊列中排隊等待被讀取。流的一個內置隊列跟蹤了那些尚未讀取的分塊
  • reader - 讀取流中分塊的對象
  • controller - 與 reader 關聯的控制對象,用來控制流(如:關閉流等操作)
  • consumer - 消費(程序):既然是可讀的,那麼就需要被讀取,讀取的過程即爲 consumer,是 reader 和它一起運行的其他處理代碼的綜合過程
特點:
  • 同一時間,consumer 只能處理一個分塊,但是能對這個分塊進行任何類型的操作
  • 一個(可讀)流只能被一個 reader 讀取,如果想被其他 reader 讀取,必須想取消掉前一個 reader
根據這些特點,拷貝(teeing)就應運而生:將一個流分割成兩個相同的副本,這兩個副本從內容上與分割器前的流是完全相同的,因此就能被兩個獨立的 reader 讀取,這個分割過程就是拷貝

(二)可寫流

與可讀流相對,其概念上是與可讀流相反,是對外輸出的

(三)其他

  • 鏈式管道(pipe chain)傳輸:這個概念呢着重在轉換上,說白了就是將一個流轉成另外一個流,當然在過程中開發者可以完成數據的操作。因此在這個過程中流至少是成對出現的。鏈式管道傳輸的起點稱爲原始源(original source),終點稱爲最終接收器(ultimate sink)
  • 背壓(backpressure):單個流或一個鏈式管道調節讀/寫速度的過程。當鏈中後面的一個流仍然忙碌,尚未準備好接受更多的分塊時,它會通過鏈向上遊的流發送一個信號,告訴上游的轉換流(或原始源)適當地減慢傳輸速度,這樣你就不會在任何地方遇到瓶頸 - 這個似乎與硬件相關,實際編程中用處似乎不大

四、流和緩衝區

這兩個從功能上沒什麼太大的差別,都是爲了解決不同運算速度的設備之間的數據傳輸問題,但是流更接近抽象的層面,屬於程序/語言方面。而緩衝區則更偏向硬件的層面,在 MDN 中("緩衝區是物理內存中的一個存儲區域,當數據進行轉移時用來臨時存放數據")中直接就將其簡化成物理內存的一塊區域

因此,如果這樣來看的話,在程序軟件到硬件的整條鏈路中,流的處理速度會比緩衝區的更快一點,即:程序 - 流 - 緩衝區 - 硬件
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章