国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

深入node之Transform

williamwen1986 / 3385人閱讀

摘要:內(nèi)部架構(gòu)上圖表示一個(gè)實(shí)例的組成部分部分緩沖數(shù)組內(nèi)部函數(shù)部分緩沖鏈表內(nèi)部函數(shù)實(shí)例必須實(shí)現(xiàn)的內(nèi)部函數(shù)以及系統(tǒng)提供的回調(diào)函數(shù)。有三個(gè)參數(shù),第一個(gè)為待處理的數(shù)據(jù),第二個(gè)為編碼,第三個(gè)為回調(diào)函數(shù)。

Transform流特性

在開發(fā)中直接接觸Transform流的情況不是很多,往往是使用相對(duì)成熟的模塊或者封裝的API來(lái)完成流的處理,最為特殊的莫過(guò)于through2模塊和gulp流操作。那么,Transform流到底有什么特點(diǎn)呢?

從名稱上說(shuō),Transform意為處理,類似于生產(chǎn)流水線上的每一道工序,每道工序針對(duì)到來(lái)的產(chǎn)品作相應(yīng)的處理;從結(jié)構(gòu)上看,Transform是一個(gè)雙工流,通俗的解釋它既可以作為可讀流,也可作為可寫流。但是,node卻對(duì)Transform流針對(duì)其特性做了更為特殊的定制,使Transform不是單純的Duplex流。

Transform流由于包含了Readable和Writeable特性,因此Transform在實(shí)際使用中有著多種方式:它既可以只作為消費(fèi)者消費(fèi)數(shù)據(jù),也可同時(shí)作為生產(chǎn)者和消費(fèi)者完成數(shù)據(jù)中間處理。下面將逐漸深入內(nèi)部闡述Transform的運(yùn)行機(jī)理及使用技巧。

Transform內(nèi)部架構(gòu)

上圖表示一個(gè)Transform實(shí)例的組成部分:Readable部分緩沖(數(shù)組)、內(nèi)部_read函數(shù)、Writeable部分緩沖(鏈表)、內(nèi)部_write函數(shù)、Transform實(shí)例必須實(shí)現(xiàn)的內(nèi)部_transform函數(shù)以及系統(tǒng)提供的回調(diào)函數(shù)afterTransform。由于Transform實(shí)例同時(shí)擁有兩部分緩沖,因此2個(gè)緩沖的存儲(chǔ)、消耗的順序也就需要了解,這對(duì)于后面使用原生Transform編寫代碼有很大的指導(dǎo)意義。

傳統(tǒng)意義的流(即Readable和Writeable)的實(shí)現(xiàn)者都需要實(shí)現(xiàn)對(duì)應(yīng)的內(nèi)部函數(shù)_read()和_write(),對(duì)于Readable實(shí)例而言,_read函數(shù)用于準(zhǔn)備從源文件中獲取數(shù)據(jù)并添加到讀緩沖中;對(duì)于Writeable實(shí)例_write函數(shù)則從寫緩沖鏈表中一次刷入到磁盤中。它們分別對(duì)應(yīng)了讀寫流程的首尾步驟,具體可以關(guān)注node中的Stream一文。

而Transform中的_read和_write函數(shù)的實(shí)現(xiàn)大有不同,由于需要兼顧流的處理,因此著重分析Transform的內(nèi)部函數(shù)執(zhí)行流程。

示例demo:
readable.pipe(transform);

以上段示例代碼為例,transform作為消費(fèi)者消費(fèi)readable。
Transform的實(shí)例transform擁有transormState和readableState屬性,保存了相關(guān)屬性,如tranform狀態(tài)信息、回調(diào)函數(shù)存儲(chǔ)和編碼等。transform作為消費(fèi)者,會(huì)在其write函數(shù)中消費(fèi)數(shù)據(jù),在node中的Stream文中介紹了write函數(shù)的實(shí)現(xiàn)細(xì)節(jié),通過(guò)內(nèi)部調(diào)用_write函數(shù)實(shí)現(xiàn)數(shù)據(jù)的寫入。而在Transform中_write函數(shù)已經(jīng)重寫:

保存transform收到的chunk數(shù)據(jù)、編碼和函數(shù)(執(zhí)行刷新寫緩沖)

在一定條件下執(zhí)行_read函數(shù)(當(dāng)狀態(tài)為非轉(zhuǎn)換下,只要讀緩沖大小未超過(guò)設(shè)定的大小,則執(zhí)行_read)

如果一切順利,readable的數(shù)據(jù)會(huì)順利執(zhí)行transform的write->_write->_read,那么原本負(fù)責(zé)填充讀緩沖的_read在Transform中發(fā)生了哪些改變呢?

Transform.prototype._read = function(n) {
  var ts = this._transformState;

  if (ts.writechunk !== null && ts.writecb && !ts.transforming) {
    ts.transforming = true;
    this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
  } else {
    // mark that we need a transform, so that any data that comes in
    // will get processed, now that we"ve asked for it.
    ts.needTransform = true;
  }
};

可見,_read的實(shí)現(xiàn)非常簡(jiǎn)單,根據(jù)條件選擇執(zhí)行_transform函數(shù)。需要注意的是_read的參數(shù)n并未有使用,因?yàn)槭欠癫迦霐?shù)據(jù)至讀緩沖是由開發(fā)者在_transform中來(lái)決定。相信大家對(duì)_transform函數(shù)并不陌生,node規(guī)定Transform實(shí)例必須提供_transform函數(shù),而該函數(shù)正是在_read中調(diào)用。

_transform有三個(gè)參數(shù),第一個(gè)為待處理的chunk數(shù)據(jù),第二個(gè)為編碼,第三個(gè)為回調(diào)函數(shù)。前兩個(gè)參數(shù)很好理解,我們可以在_transform中盡情的處理數(shù)據(jù),最后調(diào)用回調(diào)函數(shù)完成處理。那么,這個(gè)回調(diào)函數(shù)究竟是什么? 它就是Transform架構(gòu)圖中的afterTransform函數(shù),它有幾個(gè)功能:

清空各種狀態(tài)信息,如transformState對(duì)象的一些屬性,用于下次處理數(shù)據(jù)使用

可選的保存處理結(jié)果至讀緩沖區(qū)

刷新寫緩沖區(qū),執(zhí)行下一階段的數(shù)據(jù)流處理

可見,在afterTransform函數(shù)執(zhí)行后,才基本宣告transform第一階段的結(jié)束。為何是第一階段呢?因?yàn)閠ransform才完成了作為消費(fèi)者(即Writeable)的作用,如果用戶在_transform中傳入了數(shù)據(jù)到寫緩沖區(qū),那么此時(shí)transform也同時(shí)是一個(gè)生產(chǎn)者,提供數(shù)據(jù)讓后面的消費(fèi)者消費(fèi)數(shù)據(jù),這就涉及到了Transform使用上的問(wèn)題。

Transform的生產(chǎn)消費(fèi)實(shí)例
const stream = require("stream")
var c = 0;
const readable = stream.Readable({
  highWaterMark: 2,
  read: function () {
    var data = c < 26 ? String.fromCharCode(c++ + 97) : null;
    console.log("push", data);
    this.push(data);
}
})

const transform = stream.Transform({
  highWaterMark: 2,
  transform: function (buf, enc, next) {
    console.log("transform", buf.toString());
    next(null, buf);
  }
})

readable.pipe(transform);

示例代碼很簡(jiǎn)單,創(chuàng)建了一個(gè)可讀流,向消費(fèi)者提供a-z的小寫字母;創(chuàng)建了一個(gè)轉(zhuǎn)換流,在_transform函數(shù)中針對(duì)數(shù)據(jù)并不做處理僅作打點(diǎn)輸出,并向回調(diào)函數(shù)傳遞數(shù)據(jù)至讀緩沖區(qū)。我們的目的是通過(guò)transform輸出26個(gè)小寫字母,但是當(dāng)前程序執(zhí)行的結(jié)果并不讓人滿意:

執(zhí)行結(jié)果:
push a
push b
transform a
push c
transform b
push d
push e
push f

tranform僅僅處理到字母b,readable也僅僅提供了a-f的數(shù)據(jù)便戛然而止,這是為何?

這一切都?xì)w結(jié)于transform對(duì)象。認(rèn)真讀過(guò)上文后我們知道,所有的Transform實(shí)例同時(shí)有兩個(gè)緩沖區(qū),其中寫緩沖區(qū)用來(lái)接收生產(chǎn)者的數(shù)據(jù)進(jìn)行轉(zhuǎn)換操作,讀緩沖區(qū)則緩存數(shù)據(jù)給消費(fèi)者使用。而在當(dāng)前的實(shí)現(xiàn)中,transform._transform函數(shù)輸出了待處理數(shù)據(jù),同時(shí)執(zhí)行next(null, buf);。該函數(shù)上文已有分析,即afterTransform函數(shù),第一個(gè)參數(shù)為Error實(shí)例,第二個(gè)則為存入讀緩沖區(qū)的數(shù)據(jù)。在本例中,執(zhí)行完_transform后將處理后的數(shù)據(jù)存入讀緩沖區(qū),等待后面的消費(fèi)者消費(fèi)讀緩沖區(qū)的數(shù)據(jù)。可是,transform后面沒(méi)有消費(fèi)者了,因此transform在處理完字母b存入讀緩沖區(qū)后,讀緩沖區(qū)已經(jīng)滿了(設(shè)定highWaterMark為2,即讀寫緩沖區(qū)的最大值均為2字節(jié))。當(dāng)字母c、d也執(zhí)行到tranform._write后,由于不滿足執(zhí)行transform._read的條件無(wú)法執(zhí)行transform._transform函數(shù),更無(wú)法執(zhí)行afterTransform函數(shù),導(dǎo)致無(wú)法刷新寫緩沖區(qū)的數(shù)據(jù),造成字母c、d貯存在寫緩沖區(qū)。而字母e、f則由于transform的寫緩沖區(qū)滿(transform.write()返回false),只有存儲(chǔ)在readable的讀緩沖區(qū)中,等待消費(fèi)。這就造成了死循環(huán),readable和transform所有的緩沖區(qū)都滿了,流也就停止了。

解決這個(gè)問(wèn)題的方法很簡(jiǎn)單,有兩種不同方案:

transform的讀緩沖區(qū)保持為空

增加消費(fèi)者消費(fèi)transform的讀緩沖區(qū)

其實(shí)本質(zhì)上都是讓transform的讀緩沖區(qū)得到消耗。

第一種方案:

保證transform的讀緩沖區(qū)為空:
const transform = stream.Transform({
  highWaterMark: 2,
  transform: function (buf, enc, next) {
    console.log("transform", buf.toString())
    next(null, null)
  }
})

只需向next函數(shù)傳入null即可,這樣transform消費(fèi)完數(shù)據(jù)后即宣告數(shù)據(jù)處理結(jié)束,讀緩沖區(qū)始終為空。

第二種方案:

添加消費(fèi)者:
const transform = stream.Transform({
  highWaterMark: 2,
  transform: function (buf, enc, next) {
    console.log("transform", buf.toString())
    next(null, buf)
  }
})

readable.pipe(transform).pipe(process.stdout);

transform實(shí)現(xiàn)不變,只是添加了消費(fèi)者process.stdout。這樣也同時(shí)保證了transform的讀緩沖區(qū)處于可添加狀態(tài),也給了afterTransform函數(shù)刷新寫緩沖區(qū)的機(jī)會(huì),開啟新的數(shù)據(jù)處理流程。

through2的實(shí)現(xiàn)

through2的重頭戲在于Transform流,使用through2的API可方便的創(chuàng)建一個(gè)Transform實(shí)例,完成數(shù)據(jù)流的處理。

function through2 (construct) {
  return function (options, transform, flush) {
    if (typeof options == "function") {
      flush     = transform
      transform = options
      options   = {}
    }

    if (typeof transform != "function")
      transform = noop

    if (typeof flush != "function")
      flush = null

    return construct(options, transform, flush)
  }
}

module.exports = through2(function (options, transform, flush) {
  var t2 = new DestroyableTransform(options)

  t2._transform = transform

  if (flush)
    t2._flush = flush

  return t2
})

可見,through2模塊僅僅是封裝了Transform的構(gòu)造函數(shù),并封裝了更為易用的objectMode模式。之所以建議使用through2創(chuàng)建Transform對(duì)象,不僅僅是因?yàn)槠涮峁┝朔奖愕腁PI,更主要的是為了兼容性。Transform對(duì)象是屬于Stream2.0的特性,早先版本的node并沒(méi)有實(shí)現(xiàn),而通過(guò)through2創(chuàng)建的Transform實(shí)例在之前版本的node下仍可正常使用,這是由于through2并未引用node默認(rèn)提供的stream模塊,而是使用社區(qū)中較為流行的“readable-stream”模塊。

總結(jié)

本文旨在深入through2中的使用的Transform流進(jìn)行探究,并作為上一篇文章node中的stream的回顧和應(yīng)用。通過(guò)文末簡(jiǎn)單的示例了解Transform在開發(fā)中可能出現(xiàn)的問(wèn)題,學(xué)會(huì)隨意切換Transform的生產(chǎn)者和消費(fèi)者的身份,更好的指導(dǎo)實(shí)際開發(fā)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/80481.html

相關(guān)文章

  • 巧妙復(fù)制一個(gè)流

    摘要:場(chǎng)景實(shí)際業(yè)務(wù)中可能出現(xiàn)重復(fù)消費(fèi)一個(gè)可讀流的情況,比如在前置過(guò)濾器解析請(qǐng)求體,拿到進(jìn)行相關(guān)權(quán)限及身份認(rèn)證認(rèn)證通過(guò)后框架或者后置過(guò)濾器再次解析請(qǐng)求體傳遞給業(yè)務(wù)上下文。 場(chǎng)景 實(shí)際業(yè)務(wù)中可能出現(xiàn)重復(fù)消費(fèi)一個(gè)可讀流的情況,比如在前置過(guò)濾器解析請(qǐng)求體,拿到body進(jìn)行相關(guān)權(quán)限及身份認(rèn)證;認(rèn)證通過(guò)后框架或者后置過(guò)濾器再次解析請(qǐng)求體傳遞給業(yè)務(wù)上下文。因此,重復(fù)消費(fèi)同一個(gè)流的需求并不奇葩,這類似于js...

    wenzi 評(píng)論0 收藏0
  • 深入了解babel(一)

    摘要:目前羅列的只是的情況。例如,包含了。的執(zhí)行過(guò)程是,首先讀取配置中的條件,根據(jù)這些條件從模塊可得出該條件下的所有瀏覽器最低版本號(hào)列表,而又為的轉(zhuǎn)譯插件提供了瀏覽器的最低版本號(hào)列表,兩個(gè)瀏覽器版本號(hào)列表的查詢可得出一個(gè)轉(zhuǎn)譯插件的集合。 babel的定義 Babel 是 JavaScript 編譯器,更確切地說(shuō)是源碼到源碼的編譯器,通常也叫做轉(zhuǎn)換編譯器(transpiler)。 babel-...

    littleGrow 評(píng)論0 收藏0
  • 深入了解babel(一)

    摘要:目前羅列的只是的情況。例如,包含了。的執(zhí)行過(guò)程是,首先讀取配置中的條件,根據(jù)這些條件從模塊可得出該條件下的所有瀏覽器最低版本號(hào)列表,而又為的轉(zhuǎn)譯插件提供了瀏覽器的最低版本號(hào)列表,兩個(gè)瀏覽器版本號(hào)列表的查詢可得出一個(gè)轉(zhuǎn)譯插件的集合。 babel的定義 Babel 是 JavaScript 編譯器,更確切地說(shuō)是源碼到源碼的編譯器,通常也叫做轉(zhuǎn)換編譯器(transpiler)。 babel-...

    fxp 評(píng)論0 收藏0
  • gulp + gulp-better-rollup + rollup 構(gòu)建 ES6 開發(fā)環(huán)境

    摘要:地址構(gòu)建基礎(chǔ)的語(yǔ)法轉(zhuǎn)譯環(huán)境首先,安裝工具,命令如下安裝插件,由于需要作為依賴,因此,還要安裝模塊和和之間的無(wú)縫集成插件安裝核心插件安裝完成后,配置文件和文件,將這兩個(gè)文件放在項(xiàng)目根目錄下。 gulp + gulp-better-rollup + rollup 構(gòu)建 ES6 開發(fā)環(huán)境 關(guān)于 Gulp 就不過(guò)多啰嗦了。常用的 js 模塊打包工具主要有 webpack、rollup 和 br...

    XBaron 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<