在介绍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的数据。