Javascript 如何在使用承诺时打破串行循环?

Javascript 如何在使用承诺时打破串行循环?,javascript,node.js,promise,bluebird,Javascript,Node.js,Promise,Bluebird,我有一个很长的文本文件,我逐行循环以提取一些事件数据并将其存储在数据库中。该文件定期更新顶部的新数据。当这种情况发生时,我会再次运行该文件,提取新事件,但当我看到数据库中已经存在的事件时,我想停止运行(该文件总是按从最新到最旧的顺序排列) 使用问题中描述的reduce()方法,我想出了这个函数来解析文件: function parse( file) { var lines = file.split("\n"), latestDate; return li

我有一个很长的文本文件,我逐行循环以提取一些事件数据并将其存储在数据库中。该文件定期更新顶部的新数据。当这种情况发生时,我会再次运行该文件,提取新事件,但当我看到数据库中已经存在的事件时,我想停止运行(该文件总是按从最新到最旧的顺序排列)

使用问题中描述的
reduce()
方法,我想出了这个函数来解析文件:

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 */ });
}
createAsync()
数据库方法返回一个在保存事件时解析的承诺。如果事件已经存在于数据库中,它将抛出异常,这将停止承诺链,因此不会解析文件的其余部分。该异常被函数末尾的
catch()
处理程序捕获并忽略。我正在Node.js中使用Bluebird 3.0 promise库

此函数会连续循环每一行,并在遇到已保存的事件时正确停止。但我想知道这是否是在处理承诺时打破循环的最佳方式。在函数末尾吞咽抛出的异常似乎有点笨拙

欢迎提出任何改进循环处理的建议

解决方案 在此基础上,考虑到也许我应该尝试一下他对我链接到的问题的非简化答案:),我提出了这个解决方案:

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; });
        }
    });
}
循环递归被移动到一个通用函数中,
promiseach()
,该函数在数组中的每个项上循环。如果迭代器函数返回一个承诺,则在该承诺得到解决之前,不会处理下一项。如果迭代器返回
false
,则循环结束,以虚线样式:

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();
    }
}
功能承诺书(
列表
迭代器,
索引)
{
指数=指数| | 0;
if(列表和索引

我想这正是我想要的,但我想知道如果我在4000行文件上运行它,是否会出现调用堆栈问题

你所拥有的实际上根本不是一个循环

Event.createAsync
的每次调用都会立即成功返回,并带有一个承诺,这意味着您总是在整个阵列上减少

因此,此循环生成的承诺链的长度将始终是文件中的总行数,减去既不符合特定逻辑中的日期模式也不符合事件模式的行数

由于数据库中已存在事件,因此引发错误时,将终止此承诺链的异步执行

您的代码可以正常工作,但您说这是一个长文本文件,因此可能效率低下,尤其是如果提前发布是正常的,而不是异常(从您的描述中听起来像是这样)

因此,我将考虑递归方法:< /P>
function parse(file) {
  var latestDate;

  function recurse(lines, i) {
    if (i >= lines.length) return Promise.resolve();

    var line = lines[i];
    if (/* line matches date pattern */) {
      latestDate = line;
    } else if (/* line matches event pattern */) {
      return Event.createAsync(line, latestDate).then(() => recurse(lines, i + 1));
    }
    return recurse(lines, i + 1);
  }

  return recurse(file.split("\n"), 0);
}
递归方法的好处是,承诺链在
Event.createAsync
解析时异步扩展,并且仅在需要时扩展。您也可以停止调用
recurse
来停止,即不需要
Event.createAsync
来引发异常

将差异可视化的一种方法是将其与为列车铺设轨道进行比较,其中轨道代表承诺链,列车代表承诺的异步操作的执行:

使用
reduce
,您总是在列车启动前先放下整个轨道,无论列车在异常停止前沿轨道走多远。每次铺设整条赛道的费用都由你来承担(可能不多,但可以加起来)


在递归的例子中,你在移动的火车前面及时铺设下一段轨道,这样就不会浪费时间铺设不需要的轨道。

你所拥有的实际上根本不会打破循环

Event.createAsync
的每次调用都会立即成功返回,并带有一个承诺,这意味着您总是在整个阵列上减少

因此,此循环生成的承诺链的长度将始终是文件中的总行数,减去既不符合特定逻辑中的日期模式也不符合事件模式的行数

由于数据库中已存在事件,因此引发错误时,将终止此承诺链的异步执行

您的代码可以正常工作,但您说这是一个长文本文件,因此可能效率低下,尤其是如果提前发布是正常的,而不是异常(从您的描述中听起来像是这样)

因此,我将考虑递归方法:< /P>
function parse(file) {
  var latestDate;

  function recurse(lines, i) {
    if (i >= lines.length) return Promise.resolve();

    var line = lines[i];
    if (/* line matches date pattern */) {
      latestDate = line;
    } else if (/* line matches event pattern */) {
      return Event.createAsync(line, latestDate).then(() => recurse(lines, i + 1));
    }
    return recurse(lines, i + 1);
  }

  return recurse(file.split("\n"), 0);
}
递归方法的好处是,承诺链在
Event.createAsync
解析时异步扩展,并且仅在需要时扩展。您也可以停止调用
recurse
来停止,即不需要
Event.createAsync
来引发异常

将差异可视化的一种方法是将其与为列车铺设轨道进行比较,其中轨道代表承诺链,列车代表承诺的异步操作的执行:

使用
reduce
,您总是在列车启动前先放下整个轨道,无论列车在异常停止前沿轨道走多远。每次铺设整条赛道的费用都由你来承担(可能不多,但可以加起来)