在介绍Readable
时,从例子中可以发现一个现象:
生产数据时传给push
的data
是字符串或null
,
而消耗时拿到的却是Buffer
类型。
下来探讨一下流中数据类型的问题。
在创建流时,可指定objectMode
选项为true
。
此时,称为一个objectMode
流。
否则,称其为一个非objectMode
流。
更多内容见文档
Readable({ objectMode: true })
这个选项将影响push(data)
中data
的类型,以及消耗时获得的数据的类型:
- 在非
objectMode
时,data
只能是String
,Buffer
,Null
,Undefined
。 同时,消耗时获得的数据一定是Buffer
类型。 - 在
objectMode
时,data
可以是任意类型,null
仍然有其特殊含义。 同时,消耗时获得的数据与push
进来的一样。实际就是同一个引用。
所谓“缓存”,其实也就是一个数组。可以看看readable._readableState.buffer
。
每次调用push(data)
时,如果是objectMode
,便直接调用state.buffer.push(data)
。
这里,state = readable._readableState
。
如果是非objectMode
,会将String
类型转成Buffer
,再调用state.buffer.push(chunk)
。
这里,chunk
即转换后的Buffer
对象。
默认会以utf8
的编码形式进行转换。
设置方法查
文档即可。
一般不需要设置。
在消耗objectMode
流时,不管是flowing
模式,还是paused
模式,
都等同于调用state.buffer.shift()
拿到数据。
保证push
产生的数据会被一一消耗。
在消耗非objectMode
流时,flowing
模式仍然等同于调用state.buffer.shift()
。
但paused
模式则会拼接字节数,以满足readable.read(n)
中n
的要求。
如果n
没指定,则会一次将state.buffer
所有字节拼起来消耗掉。
这里看一个比较有意思的例子来说明objectMode
与非objectMode
的区别。
非objectMode
下push('')
var Stream = require('stream')
var source = ['a', '', 'c']
var readable = Stream.Readable({
read: function () {
var data = source.shift()
data = data == null ? null : data
this.push(data)
},
})
readable.on('end', function () {
console.log('end')
})
readable.on('data', function (data) {
console.log('data', data)
})
输出:
⌘ node example/empty-string-non-objectMode.js
data <Buffer 61>
data <Buffer 63>
end
objectMode
下push('')
var Stream = require('stream')
var source = ['a', '', 'c']
var readable = Stream.Readable({
objectMode: true,
read: function () {
var data = source.shift()
data = data == null ? null : data
this.push(data)
},
})
readable.on('end', function () {
console.log('end')
})
readable.on('data', function (data) {
console.log('data', data)
})
输出:
⌘ node example/empty-string-objectMode.js
data a
data
data c
end
可见,非objectMode
下直接将push('')
给忽略了,
而objectMode
下在消耗时能拿到这个空字符串。
(注意,非objectMode
时push('')
实际是会修改内部状态的,
会有一定的副作用,一般不要如此。
见这里)
要点
objectMode
时,可push
任意类型的数据,消耗时会逐个消耗同样的数据- 非
objectMode
时,只能push
以下数据类型:String
,Buffer
,Null
,Undefined
。 在消耗时只能拿到Buffer
类型的数据
Writable({ objectMode: true })
这个选项将影响write(data)
中data
的类型,以及底层消耗时获得的数据(_write(chunk, _, next)
中的chunk
)的类型:
- 在非
objectMode
时,data
只能是String
,Buffer
,Null
,Undefined
。 同时,chunk
一定是Buffer
类型。 - 在
objectMode
时,data
可以是任意类型,null
仍然有其特殊含义。 同时,chunk
即data
。
非objectMode
:
var Stream = require('stream')
var writable = Stream.Writable({
write: function (data, _, next) {
console.log(data)
process.nextTick(next)
},
})
writable.write('a')
writable.write('b')
writable.write('c')
writable.end()
输出:
⌘ node example/writable.js
<Buffer 61>
<Buffer 62>
<Buffer 63>
objectMode
:
⌘ node example/writable-objectMode.js
a
b
c
正如文档
中所言,Node.js核心模块没有使用objectMode
的,只有Node.js的用户才会用到。
具体某个流是否应当设置objectMode
,需要看其所处的上下游。
如果上游是objectMode
,且输出的是非String
或Buffer
,那就必须用objectMode
。
如果下游不是objectMode
,就必须注意,不要输出非String
或Buffer
的数据。