[Javascript] ¿Cómo salir de un bucle en serie al usar promesas?


Answers

Question

Tengo un archivo de texto largo que recorro línea por línea para extraer algunos datos de eventos y almacenarlos en una base de datos. El archivo se actualiza periódicamente con nuevos datos en la parte superior. Cuando eso sucede, vuelvo a ejecutar el archivo extrayendo los nuevos eventos, pero quiero parar cuando llego a un evento que ya está en la base de datos (el archivo siempre se ordena de más nuevo a más viejo).

Usando el enfoque de reduce() se describe en esta respuesta a la pregunta La forma correcta de escribir bucles para promesas , se me ocurrió esta función para analizar el archivo:

function parse(
    file)
{
    var lines = file.split("\n"),
        latestDate;

    return lines.reduce(function(promise, line) {
        return promise.then(function() {
            if (/* line matches date pattern */) {
                latestDate = line;
            } else if (/* line matches event pattern */) {
                return Event.createAsync(line, latestDate);
            }

            return promise;
        });
    }, Promise.resolve())
        .catch({ errorName: "uniqueViolated" }, 
            function() { /* ignore only the createAsync error */ });
}

El método de base de datos createAsync() devuelve una promesa que se resuelve cuando se guarda el evento. Lanzará una excepción si el evento ya existe en la base de datos, lo que detiene la cadena de promesas para que el resto del archivo no se analice. Esa excepción es capturada e ignorada por el controlador catch() al final de la función. Estoy usando la biblioteca de la promesa Bluebird 3.0 en Node.js.

Esta función recorre cada línea en serie y se detiene correctamente cuando golpea un evento ya guardado. Pero me pregunto si esta es la mejor manera de salir de un bucle mientras lidia con las promesas. Tragar la excepción lanzada al final de la función parece un poco kludgy.

Cualquier sugerencia para mejorar el manejo del bucle es bienvenida.

¿Solución?

Sobre la base de la respuesta de jib , y teniendo en cuenta el comentario de Bergi de que tal vez debería haber intentado su respuesta no reducida a la pregunta con la que me conecté :), se me ocurrió esta solución:

function parse(
    file)
{
    var lines = file.split("\n"),
        latestDate;

    return promiseEach(lines, function(line) {
        if (/* line matches date pattern */) {
            latestDate = line;
        } else if (/* line matches event pattern */) {
            return Event.createAsync(line, latestDate);
                .catch({ errorType: "uniqueViolated" }, function() { return false; });
        }
    });
}

La recursión de bucle se mueve a una función genérica, promiseEach() , que recorre todos los elementos de una matriz. Si la función del iterador devuelve una promesa, el siguiente elemento no se procesa hasta que se resuelva esa promesa. Si el iterador devuelve false , entonces el ciclo termina, estilo Lo-dash:

function promiseEach(
    list,
    iterator,
    index)
{
    index = index || 0;

    if (list && index < list.length) {
        return Promise.resolve(iterator(list[index])).then(function(result) {
            if (result !== false) {
                return promiseEach(list, iterator, ++index);
            }
        });
    } else {
        return Promise.resolve();
    }
}

Creo que hace lo que quiero, pero me pregunto si habrá problemas con la pila de llamadas si la ejecuto en un archivo de 4000 líneas.






Links