我有一个长文本文件,我逐行循环以提取一些事件数据并将其存储在数据库中。该文件定期更新,顶部显示新数据。发生这种情况时,我会再次运行文件以提取新事件,但是当我遇到数据库中已有的事件时,我想停止操作(文件总是按最新到旧的顺序排序)。
使用reduce()
中描述的this answer方法解决问题Correct way to write loops for promise,我想出了此功能来分析文件:
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()
数据库方法返回一个在保存事件时已解决的承诺。如果该事件已经存在于数据库中,它将引发异常,这将终止Promise链,因此不会解析文件的其余部分。该异常在函数末尾被catch()
处理程序捕获并忽略。我正在Node.js中使用Bluebird 3.0 Promise库。
此函数确实按顺序循环遍历每行,并在遇到已保存的事件时正确停止。但是我想知道这是否是在处理承诺时突破循环的最佳方法。在函数末尾吞下引发的异常似乎有点混乱。
欢迎提出任何改进循环处理的建议。
基于jib's answer,并考虑到Bergi's comment,也许我应该尝试对我链接到的问题进行非简化的回答:),我想出了以下解决方案:
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; });
}
});
}
循环递归被移入通用函数promiseEach()
中,该函数遍历数组中的每个项目。如果迭代器函数返回了一个Promise,则在该Promise解析之前,不会处理下一项。如果迭代器返回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();
}
}
我想这就是我想要的,但是我想知道如果在4000行文件上运行它是否会出现调用堆栈问题。
您拥有的东西实际上根本不会跳出循环。
每次对Event.createAsync
的调用都会立即成功返回一个Promise,这意味着您总是减少entire数组。
因此,此循环产生的承诺链的长度将始终是文件中的总行数,减去特定逻辑中既不适合日期也不适合事件模式的行数。
这是此promise链的异步执行,由于数据库中已存在事件而在引发错误时,该请求链随后会终止。
您的代码有效,但是您说这是一个长文本文件,所以它可能效率不高,特别是如果尽早突破是规范而不是例外(听起来像是您的描述)。
因此,我将考虑使用递归方法:
可能是将其与铺设火车的轨道进行比较,其中轨道代表承诺链,而火车则表示所承诺的异步操作的执行: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
,您总是总是在火车开始前先放下整个轨道,而不管火车在异常停止之前最终沿着轨道走下多远。您每次都要花费铺设整个轨道的成本(虽然可能不多,但可以累加)。
在recurse
示例中,您像Gromit in the finale of "The Wrong Trousers"一样在移动火车的前面即时铺设了下一条轨道,因此不会浪费时间来铺设不需要的轨道。