У меня есть программа Node.js Typcript, в которой я пытаюсь разобрать большие файлы CSV по строкам и делать что-то с этими строками асинхронно. В частности, мне нужна функция, которая будет:
Некоторые требования и соображения:
Вот несколько тестовых кодов, с которыми я работал. ObjectStream
- это настраиваемое преобразование Node.js, которое преобразует CSV-строки в объекты.
function parseFileAsync(filePath: string): Promise<any> {
var doParseFileAsync = (filePath: string) => {
var streamDeferred = q.defer<Promise<any>[]>();
var promises: Promise<any>[] = [];
var propertyNames: string[] = [];
var stream = fs.createReadStream(filePath, { encoding: "utf8" })
.pipe(new LineStream({ objectMode: true }))
.pipe(new ObjectStream({ objectMode: true }));
stream.on("readable", () => {
var obj: Object;
while ((obj = stream.read()) !== null) {
console.log('\nRead an object...');
var operationDeferred = q.defer<any>();
operationDeferred.resolve(doSomethingAsync(obj));
promises.push(operationDeferred.promise);
}
});
stream.on("end", () => {
streamDeferred.resolve(promises);
});
return streamDeferred.promise;
}
return doParseFileAsync(filePath)
.then((result: Promise<any>[]) => {
return q.all(result);
});
}
parseFileAsync(filePath)
.done((result: any[]) => {
console.log('\nFinished reading and processing the file:\n\t${result.toString()}');
});
Заключительный done
вызов выполняется до того, как поток даже начнет работать, потому что parseFileAsync
немедленно выполняет пустой массив; поток еще не успел подтолкнуть какие-либо обещания.
После нескольких дней поиска я все еще не уверен, какой правильный способ сделать это. Эксперты узла /JavaScript: помощь?
Код был обновлен, и мои обещания теперь играют хорошо. Однако мне нужен способ подключиться к потоку и, при желании, отменить процесс. Мне также нужен способ повторить любые неудачные операции.
Я сталкивался с некоторыми ограничениями в программной архитектуре, которые не позволяли мне передавать обещания так же свободно, как я хотел. Вместо этого, вместо того, чтобы отпустить кучу обещаний, я решил подождать, пока предыдущая партия не закончится, прежде чем начинать новую. Здесь подход, который я принял:
Разделите поток в свою собственную функцию, которая принимает токены продолжения. Возвращаемое значение будет содержать прочитанные данные, а также токен продолжения, если есть больше данных для чтения:
function readFile(filepath: string, lines: number, start: any): Promise<any> {
...
}
Определите функцию, которая будет запускать операцию с повтором. Внутри этой функции извлекают и обрабатывают кусок данных из файла. Если результат имеет токен продолжения, "рекурсивно" снова вызовет функцию операции:
function processFile(filepath: string, next: any): Promise<any> {
var chunkSize = 1;
return readLines(filepath, chunkSize, next)
.then((result) => {
// Do something with 'result.lines'
...
if (result.next) {
return parseFile(filepath, result.next);
}
});
}
И вуаля! Долгосрочная операция, которая работает на кусках, и легко сообщить о прогрессе.
map stream
в парадигме потока. Если вы должны использовать обещания (возможно, потребитель ожидает обещание), просто верните обещание и разрешайте его всякий раз, когда все ваши потоки завершаются.