是否存在使用ES6生成器使流可迭代的模式?
请参阅下面的“MakeStreamIterable”。
import {createReadStream} from 'fs'
let fileName = 'largeFile.txt'
let readStream = createReadStream(fileName, {
encoding: 'utf8',
bufferSize: 1024
})
let myIterableAsyncStream = MakeStreamIterable(readStream)
for (let data of myIterableAsyncStream) {
let str = data.toString('utf8')
console.log(str)
}
我对 co </ em>或bluebird的 coroutine 不感兴趣,或者对 deasync 进行阻止。
黄金是 MakeStreamIterable 应该是一个有效的函数。
答案 0 :(得分:7)
是否存在使用ES6生成器使流可迭代的模式?
不,这是无法实现的,因为发电机是同步的。他们必须知道他们正在产生什么以及何时产生。目前只能通过使用某种基于回调的实现来实现异步数据源的迭代。因此,无法使MakeStreamIterable
&#39;成为有效的功能&#39;如果您的意思是“有效的函数,其结果可以提供给for-of
循环”。
流表示在可能无限长的时间内异步接收的潜在无限量数据。如果我们看一下definition of an iterator on MDN,我们就可以更详细地定义一条流,让它变得难以理解&#39;:
当知道如何一次从一个集合中访问项目时,对象是一个迭代器,而跟踪其在该序列中的当前位置。在JavaScript中,迭代器是提供next()方法的对象,它返回序列中的下一个项目。此方法返回一个具有两个属性的对象:
done
和value
。
(强调是我自己的。)
让我们从这个定义中挑选出一个可迭代的属性。对象必须......
next
,该方法检索具有属性的对象,该属性包含序列中的下一个value
或通知迭代为done
。流不符合上述任何条件,因为......
next
可以使用的for-of
方法。______
我们无法实际迭代从流中接收的数据(绝对不使用for-of
),但我们可以构建一个假装通过使用Promises(yay!)并在闭包内抽象出流的事件处理程序。
// MakeStreamIterable.js
export default function MakeStreamIterable (stream) {
let collection = []
let index = 0
let callback
let resolve, reject
stream
.on('error', err => reject && reject(err))
.on('end', () => resolve && resolve(collection))
.on('data', data => {
collection.push(data)
try {
callback && callback(data, index++)
} catch (err) {
this.end()
reject(err)
}
})
function each (cb) {
if(callback) {
return promise
}
callback = (typeof cb === 'function') ? cb : null
if (callback && !!collection) {
collection.forEach(callback)
index = collection.length
}
return promise
}
promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
promise.each = each
return promise
}
我们可以像这样使用它:
import {myIterableAsyncStream} from './MakeStreamIterable'
let myIterableAsyncStream = MakeStreamIterable(readStream)
myIterableAsyncStream
.each((data, i) => {
let str = data.toString('utf8')
console.log(i, str)
})
.then(() => console.log('completed'))
.catch(err => console.log(err))
有关此实施的注意事项:
each
。each
时,在调用之前收到的所有值都将逐个传递给回调forEach
- 样式。之后,所有后续数据将立即传递给回调。collection
数据,这意味着如果由{提供的迭代方法,我们实际上根本不必调用each
{1}}并不令人满意。答案 1 :(得分:1)
很快你就可以使用Async Iterators and Generators了。在节点9.8中,您可以通过运行--harmony
命令行选项来使用它。
async function* streamAsyncIterator(stream) {
// Get a lock on the stream
const reader = stream.getReader();
try {
while (true) {
// Read from the stream
const {done, value} = await reader.read();
// Exit if we're done
if (done) return;
// Else yield the chunk
yield value;
}
}
finally {
reader.releaseLock();
}
}
async function example() {
const response = await fetch(url);
for await (const chunk of streamAsyncIterator(response.body)) {
// …
}
}
感谢Jake Archibald上面的examples。
答案 2 :(得分:0)
看起来流在将来会“自然地”迭代-只是等待浏览器实现它:
for await (const chunk of stream) {
...
}