>我有来自readdirp
模块的目录流。
我想:-
- 使用正则表达式搜索文件(例如
README.*
) 在每个目录中 - 读取该文件中不以
#
开头的第一行 - 打印出每个目录和目录中自述文件的第一非标题行。
我正在尝试使用溪流和高原.js来做到这一点。
我被困在尝试处理每个目录中所有文件的流时。
h = require 'highland'
dirStream = readdirp root: root, depth: 0, entryType: 'directories'
dirStream = h(dirStream)
.filter (entry) -> entry.stat.isDirectory()
.map (entry) ->
# Search all files in the directory for README.
fileStream = readdirp root: entry.fullPath, depth: 0, entryType: 'files', fileFilter: '!.DS_Store'
fileStream = h(fileStream).filter (entry) -> /README..*/.test entry.name
fileStream.each (file) ->
readmeStream = fs.createReadStream file
_(readmeStream)
.split()
.takeUntil (line) -> not line.startsWith '#' and line isnt ''
.last(1)
.toArray (comment) ->
# TODO: How do I access `comment` asynchronously to include in the return value of the map?
return {name: entry.name, comment: comment}
最好将高地流视为不可变的,并且像filter
和map
这样的操作返回依赖于旧流的新流,而不是对旧流的修改。
此外,Highland 方法也很懒惰:您应该仅在现在绝对需要数据时才调用 each
或 toArray
。
异步映射流的标准方法是flatMap
。这就像map
,但你给它的函数应该返回一个流。从flatMap
获取的流是所有返回流的串联。由于新流按顺序依赖于所有旧流,因此可用于对异步过程进行排序。
我会将您的示例修改为以下内容(澄清了一些变量名称):
h = require 'highland'
readmeStream = h(readdirp root: root, depth: 0, entryType: 'directories')
.filter (dir) -> dir.stat.isDirectory()
.flatMap (dir) ->
# Search all files in the directory for README.
h(readdirp root: dir.fullPath, depth: 0, entryType: 'files', fileFilter: '!.DS_Store')
.filter (file) -> /README..*/.test file.name
.flatMap (file) ->
h(fs.createReadStream file.name)
.split()
.takeUntil (line) -> not line.startsWith '#' and line isnt ''
.last(1)
.map (comment) -> {name: file.name, comment}
让我们浏览一下此代码中的类型。首先,请注意flatMap
具有类型(在哈斯克利符号中)Stream a → (a → Stream b) → Stream b
,即它接受一个包含一些类型a
的事物的流,以及一个期望类型为a
的事物并返回包含b
s的流的函数,并返回一个包含b
s的流。集合类型(如流和数组)的标准是将flatMap
实现为连接返回的集合。
h(readdirp root: root, depth: 0, entryType: 'directories')
假设这有类型 Stream Directory
.filter
不会更改类型,因此flatMap
将Stream Directory → (Directory → Stream b) → Stream b
。我们将看到函数返回的内容:
h(readdirp root: dir.fullPath, depth: 0, entryType: 'files', fileFilter: '!.DS_Store')
称之为Stream File
,所以第二个flatMap
是Stream File → (File → Stream b) → Stream b
。
h(fs.createReadStream file.name)
这是一个Stream String
. split
,takeUntil
和last
不会改变这一点,那么map
会怎么做呢? map
与flatMap
非常相似:它的类型是 Stream a → (a → b) → Stream b
。在本例中,a
String
,b
是对象类型{name : String, comment : String}
。然后map
返回该对象的流,这是整个flatMap
函数返回的内容。向上一步,第二个flatMap
中的b
是对象,所以第一个flatMap
的函数也返回对象的一个流,所以整个流就是一个Stream {name : String, comment : String}
。
请注意,由于Highland的懒惰,这实际上并没有启动任何流式传输或处理。需要使用each
或toArray
来引发thunk
并启动管道。在 each
中,回调将使用您的对象调用。根据您要对注释执行的操作,最好再flatMap
一些(例如,如果要将它们写入文件)。
好吧,我不是故意写一篇文章。希望这有帮助。