使用promise时如何突破串行循环?

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

使用这个回答中描述的reduce()方法的问题正确的方式来编写promise的循环 ,我想出了这个函数来parsing文件:

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

这个函数可以顺序循环遍历每一行,并在它碰到一个已经保存的事件时正确停止。 但是我想知道这是否是在处理承诺时突破循环的最好方法。 在函数结尾处吞吐抛出的exception看起来有点笨拙。

任何改善循环处理的build议都是值得欢迎的。

解?

基于jib的答案 ,并考虑到Bergi的评论 ,也许我应该只是尝试了他的non-reduce回答我链接到的问题:),我想出了这个解决scheme:

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

循环recursion被移入一个通用函数promiseEach() ,该函数promiseEach()数组中的每个项目。 如果迭代器函数返回一个承诺,那么在承诺parsing之前不会处理下一个项目。 如果迭代器返回false ,则循环结束,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(); } } 

我认为这是我想要的,但我想知道是否会有调用堆栈问题,如果我通过4000行文件运行它。

你有什么实际上并没有打破一个循环。

每次调用Event.createAsync都会立即成功返回一个promise,这意味着您总是减less整个数组。

由这个循环产生的承诺链的长度将因此总是文件中行的总数,减去在你的特定逻辑中不适合date和事件模式的行数。

这是承诺链的asynchronous执行 ,因为数据库中已经存在一个事件,所以在抛出错误的时候会终止。

你的代码可以工作,但是你说这是一个长文本文件,所以它可能是低效率的,特别是如果早期突破是规范而不是exception(从你的描述中听起来像这样)。

因此,我会考虑一个recursion的方法:

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

recursion方法的好处在于,当Event.createAsyncparsing时,并且仅在需要时,承诺链才会asynchronous扩展。 你也可以只停止调用recurse来停止,即不需要Event.createAsync抛出exception。

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

reduce下,无论火车在exception停止之前最终沿着轨道走多远,在火车开始之前,都要先放下整个轨道。 你每次都吃掉整条赛道的成本(可能不多,但可以加起来)。

举例来说,你正准备在行驶的列车前面准备下一段轨道,就像在“错误的裤子”中的Gromit一样,所以不需要浪费时间来铺设不需要的轨道。