摘要:總結(jié)雖然我沒有像老師一樣進(jìn)行性能測試,但是猜測也知道的方式在數(shù)量比較小的時候肯定要弱于正常方式,的好處在于數(shù)據(jù)量比較大的時候,可以使用比較小的內(nèi)存,盡快的處理數(shù)組中前置的數(shù)據(jù)。
TLDR;
這篇文章的風(fēng)格是在致敬 Jim 老師;致敬,致敬,懂嗎,不是抄襲,程序員的事怎么能叫抄襲。
當(dāng)然我對 Node.js 的 stream 也是現(xiàn)學(xué)現(xiàn)賣,有使用不當(dāng)?shù)牡胤剑凑堉赋觥?br>原文鏈接 歡迎 star。
寫這篇文章的初衷是年前看 SICP 的時候,第二章介紹構(gòu)造數(shù)據(jù)抽象的時候有提到 Lisp 對序列的處理采用類似『信號流』的方式。所以很自然的就想到了 Node.js 中的 pipe 方式,于是就一直想用 pipe 的方式嘗試一下。
同 Jim 老師的這篇 文章 中描述的一樣, 我也是懶癌發(fā)作,從年尾拖到今年年初,然后在年初又看到了 Jim 老師 的博客,深受啟發(fā),終于下定決心要開始碼了...... 然后,嗯,又拖到昨天。促使我下定決心要寫的主要原因是昨天部門的年會!反正年會跟我這種死肥宅也沒多大關(guān)系,在大家 happy 的時候構(gòu)思了下代碼實現(xiàn),回家用了一晚上的時候補(bǔ)上了代碼。
Jim 老師在他的文章里面也說了,JS 的那些數(shù)組操作 (map/ reduce/filter) 啥的,每次調(diào)用的時候都會進(jìn)行一次完整的遍歷。試想一下如果有一個第一個數(shù)是1,長度是 1億 的遞增為 1 的數(shù)組,需要把所有的數(shù)組都乘 3,再排除其中的奇數(shù),如果用 (map/filter) 的方法,只要也需要循環(huán) 一億五千萬次;那么如果有其他辦法能只循環(huán)一億次,是不是節(jié)省了大量的內(nèi)存資源和循環(huán)消耗的時間。
廢話不多說,直接上代碼吧。
pipe在編寫代碼時,我們應(yīng)該有一些方法將程序像連接水管一樣連接起來 -- 當(dāng)我們需要獲取一些數(shù)據(jù)時,可以去通過"擰"其他的部分來達(dá)到目的。這也應(yīng)該是IO應(yīng)有的方式。 -- Doug McIlroy. October 11, 1964
關(guān)于 node 的 stream 可以看看這篇 文章。
下面是代碼部分,整個代碼我是在邊學(xué) pipe 邊用一晚上的時間倉促寫就的,懶癌發(fā)作,也不想再重構(gòu)了,各位相公講究看吧,求別噴代碼。
入口const stream = require("stream") const last = Symbol() // 在 selfArray 中接收一個真正的數(shù)組 // 返回一個可讀流 // 如果再做的精細(xì)點,可以做成可讀可寫流,這樣就能通過控制流的大小,來控制內(nèi)存的大小,別幾億條數(shù)據(jù)直接撐爆內(nèi)存了 // 不過對后面 reduce 的處理就比較麻煩 function selfArray(a) { const rs = new stream.Readable({ objectMode: true }) a.forEach((v, index) => { rs.push(v) }) rs.push(last) rs.push(null) return rs }
上面的 selfArray 在流的最后面 push 了一個 Symbol 對象來標(biāo)志整個流的輸入結(jié)束,留待為之后 reduce 的使用。
Map/Filter/Reduce 的實現(xiàn)function forEach(callback) { const ws = new stream.Writable({ objectMode: true }) let index = 0 ws._write = function (chunk, enc, next) { if (chunk !== last) { callback(chunk, index++) next() } } return ws } function filter(callback) { const trans = new stream.Transform({ readableObjectMode: true, writableObjectMode: true }) let index = 0 trans._transform = function (chunk, enc, next) { if (chunk === last) { next(null, last) } else { let condition = callback(chunk, index++) if (condition) { this.push(chunk) } next() } } return trans } function map(callback) { const trans = new stream.Transform({ readableObjectMode: true, writableObjectMode: true }) let index = 0 trans._transform = function (chunk, enc, next) { if (chunk === last) { next(null, last) } else { next(null, callback(chunk, index++)) } } return trans } function reduce(callback, initial) { const trans = new stream.Transform({ readableObjectMode: true, writableObjectMode: true }) let index = 0, current = initial, prev = initial trans._transform = function (chunk, enc, next) { if (chunk === last) { if (index > 1) { prev = callback(prev, current, index - 1) } this.push(prev) this.push(last) return next(null, last) } if (initial === void 0 && index === 0) { prev = chunk } if (index > 0) { prev = callback(prev, current, index - 1) } current = chunk index++ next() } return trans }
上面的代碼在 reduce 的實現(xiàn)稍微麻煩了一些,reduce 對沒有初始值,原始數(shù)組為空的條件下有各種不同的處理情況,翻看了下 MDN 的解釋又自己實現(xiàn)了下。
使用selfArray([9, 2, 6, 3, 5, 6, 7, 1, 4, 4]) .pipe(map(v => v * 3)) .pipe(filter(v => v % 2)) .pipe(reduce((p, c) => p + c, 0)) .pipe(forEach(v => { console.log("pipe 計算最后的結(jié)果是:", v) }))
為了好看我故意把各種括號都刪掉了。嗯,看起來還挺完美,我們來測試下
selfArray([9, 2, 6, 3, 5, 6, 7, 1, 4, 4]) .pipe(map(v => { console.log("map:", v) return v * 3 })) .pipe(filter(v => { console.log("filter:", v) return v % 2 })) .pipe(reduce((p, c) => { console.log("reduce:", p, c) return p + c }, 0)) .pipe(forEach(v => { console.log("pipe 計算最后的結(jié)果是:", v) })) 加上 log 之后可以看到結(jié)算結(jié)果是: map: 9 filter: 27 map: 2 filter: 6 map: 6 filter: 18 map: 3 filter: 9 reduce: 0 27 map: 5 filter: 15 reduce: 27 9 map: 6 filter: 18 map: 7 filter: 21 reduce: 36 15 map: 1 filter: 3 reduce: 51 21 map: 4 filter: 12 map: 4 filter: 12 reduce: 72 3 pipe 計算最后的結(jié)果是: 75
從上面的 log 可以看到, 第一個數(shù) 9 先執(zhí)行了 map,然后在 3 之后就直接進(jìn)入了 filter,此時第 2 個數(shù) 2 也開始被 map 處理,然后被 filter 處理,但是由于 3 之后是偶數(shù)不會被 reduce 接收, reduce 會一直等到第二個奇數(shù),也就是 3 進(jìn)入之后才會被處理... 嗯,直到最終的計算結(jié)果是 75, 被 forEach 消耗。
總結(jié)雖然我沒有像 Jim 老師一樣進(jìn)行性能測試,但是猜測也知道 pipe 的方式在數(shù)量比較小的時候肯定要弱于正常方式,pipe 的好處在于數(shù)據(jù)量比較大的時候,可以使用比較小的內(nèi)存,盡快的處理數(shù)組中前置的數(shù)據(jù)。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/88185.html
摘要:在行中,我們將子進(jìn)程的連接到當(dāng)前進(jìn)程的。等待子進(jìn)程通過退出函數(shù)如下所示。子進(jìn)程的實現(xiàn)以下代碼用異步寫入以命令運行的子進(jìn)程的我們?yōu)槊钌梢粋€名為的獨立進(jìn)程。而是子進(jìn)程完成。沒有這個,將會在調(diào)用之前被輸出。 翻譯:瘋狂的技術(shù)宅原文:http://2ality.com/2018/05/chi... 本文首發(fā)微信公眾號:jingchengyideng歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)...
摘要:因此,你還是需要各種各樣雜七雜八的工具來轉(zhuǎn)換你的代碼噢,我可去你媽的吧,這些東西都是干嘛的我就是想用個模塊化,我到底該用啥子本文正旨在列出幾種可用的在生產(chǎn)環(huán)境中放心使用模塊化的方法,希望能幫到諸位后來者這方面的中文資源實在是忒少了。 原文發(fā)表在我的博客上。最近搗鼓了一下 ES6 的模塊化,分享一些經(jīng)驗 :) Python3 已經(jīng)發(fā)布了九年了,Python 社區(qū)卻還在用 Python 2...
摘要:服務(wù)器每收到一條請求,都會用新的和對象觸發(fā)請求回調(diào)函數(shù)。在調(diào)用完請求回調(diào)函數(shù)之后,就要由你負(fù)責(zé)用方法結(jié)束響應(yīng)。 前言 本文將介紹Node.js的一些基本概念,包含它的歷史,特性和簡單的使用等。如果你有過服務(wù)端的編程經(jīng)驗,那么你將能很快熟悉它。 Node.js是什么 showImg(https://segmentfault.com/img/bVbc24J?w=619&h=35); 這是N...
摘要:當(dāng)一個客戶端的響應(yīng)對象是一個可讀流,那么在服務(wù)器端這就是一個可寫流。的模塊給我們提供了一個可以操作任何文件的可讀流通過方法創(chuàng)建。創(chuàng)建一個可讀流創(chuàng)建可讀流,我們需要類創(chuàng)建一個可讀流非常簡單。可以通過修改可讀流配置里面的方法實現(xiàn)。 Node.js的stream模塊是有名的應(yīng)用困難,更別說理解了。那現(xiàn)在可以告訴你,這些都不是問題了。 多年來,開發(fā)人員在那里創(chuàng)建了大量的軟件包,其唯一目的就是使...
摘要:核心概念流流,簡單來說就是建立在面向?qū)ο蠡A(chǔ)上的一種抽象的處理數(shù)據(jù)的工具。類型,設(shè)置輸出路徑以某個路徑的某個組成部分為基礎(chǔ)向后拼接。 一、gulp簡介 1.gulp是什么? gulp是前端開發(fā)過程中一種基于流的代碼構(gòu)建工具,是自動化項目的構(gòu)建利器;它不僅能對網(wǎng)站資源進(jìn)行優(yōu)化,而且在開發(fā)過程中很多重復(fù)的任務(wù)能夠使用正確的工具自動完成;使用它,不僅可以很愉快的編寫代碼,而且大大提高我們的工...
閱讀 3684·2021-10-09 09:44
閱讀 3349·2021-09-22 15:29
閱讀 3117·2019-08-30 15:54
閱讀 3018·2019-08-29 16:19
閱讀 2142·2019-08-29 12:50
閱讀 595·2019-08-26 14:04
閱讀 1696·2019-08-23 18:39
閱讀 1345·2019-08-23 17:59