Как объединить обещания, сгенерированные из асинхронных функций в обратном вызове потока Node.js?

0

У меня есть программа Node.js Typcript, в которой я пытаюсь разобрать большие файлы CSV по строкам и делать что-то с этими строками асинхронно. В частности, мне нужна функция, которая будет:

  1. Откройте CSV файл.
  2. Разберите следующую строку с объектом.
  3. (В идеале) Соберите заданное количество объектов для пакетной обработки.
  4. Передайте объект функции async для обработки (возвращает обещание).
  5. Соберите обещания из функции обработки.

Некоторые требования и соображения:

  • Мне нужно опросить любые из этих обещаний о прогрессе.
  • Предположим, что эти файлы 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: помощь?

Обновить

Код был обновлен, и мои обещания теперь играют хорошо. Однако мне нужен способ подключиться к потоку и, при желании, отменить процесс. Мне также нужен способ повторить любые неудачные операции.

  • 0
    Моя первоначальная мысль (не вдаваясь в подробности) заключается в том, что вы должны выбрать ту или иную парадигму. Кажется, это лучше всего обрабатывается чем-то вроде map stream в парадигме потока. Если вы должны использовать обещания (возможно, потребитель ожидает обещание), просто верните обещание и разрешайте его всякий раз, когда все ваши потоки завершаются.
  • 0
    Создайте новое обещание P, которое будет выполнено после завершения потока, добавив его обещания в список. Верните P и разрешите список обещаний. Подождите P, затем дождитесь всех результатов P.
Показать ещё 3 комментария
Теги:
promise
stream

1 ответ

0
Лучший ответ

Я сталкивался с некоторыми ограничениями в программной архитектуре, которые не позволяли мне передавать обещания так же свободно, как я хотел. Вместо этого, вместо того, чтобы отпустить кучу обещаний, я решил подождать, пока предыдущая партия не закончится, прежде чем начинать новую. Здесь подход, который я принял:

  1. Разделите поток в свою собственную функцию, которая принимает токены продолжения. Возвращаемое значение будет содержать прочитанные данные, а также токен продолжения, если есть больше данных для чтения:

    function readFile(filepath: string, lines: number, start: any): Promise<any> {
        ...
    }
    
  2. Определите функцию, которая будет запускать операцию с повтором. Внутри этой функции извлекают и обрабатывают кусок данных из файла. Если результат имеет токен продолжения, "рекурсивно" снова вызовет функцию операции:

    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);
                }
            });
    }
    

И вуаля! Долгосрочная операция, которая работает на кусках, и легко сообщить о прогрессе.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню