是否有一种纯粹的基于 Promise 的映射/连接集合的方法?

Posted

技术标签:

【中文标题】是否有一种纯粹的基于 Promise 的映射/连接集合的方法?【英文标题】:Is there a pure Promise-based approach for mapping/concatenating collections? 【发布时间】:2013-08-03 18:55:06 【问题描述】:

一般来说异步与 Q

我正在学习 Node.js 开发,并试图围绕管理异步“回调地狱”的策略展开思考。我探索的两个主要策略是 Caolan McMahon 的 async 模块和 Kris Kowal 的基于 promise 的 Q 模块。

就像many other people 一样,我仍在努力理解何时应该使用一个与另一个。但是,总的来说,我发现 promises 和基于 Q 的代码稍微更直观,所以我一直在朝着这个方向前进。

映射/连接集合一般

但是,我仍然无法使用异步模块的 functions for managing collections。来自 Java 和 Python 背景,大多数时候我使用集合时,逻辑如下所示:

    初始化一个新的空集合,用于存储结果。 对旧集合执行 for-each 循环,对每个元素应用一些逻辑并将其结果推送到新的空集合中。 当 for-each 循环结束时,继续使用新的集合。

在客户端 javascript 中,我已经习惯于使用 jQuery 的 map() function... 传入第 2 步的逻辑,并将第 3 步的结果作为返回值。感觉就像是相同的基本方法。

使用 async 和 Q 映射/连接集合

Node 端异步模块具有类似的 map 和 concat 函数,但它们不会将连接的结果返回到原始范围级别。您必须改为进入回调地狱才能使用结果。示例:

var deferred = Q.defer();

...

var entries = [???]; // some array of objects with "id" attributes

async.concat(entries, function (entry, callback) 
    callback(null, entry.id);
, function (err, ids) 
    // We now have the "ids" array, holding the "id" attributes of all items in the "entries" array.
    ...
    // Optionaly, perhaps do some sorting or other post-processing on "ids".
    ...
    deferred.resolve(ids);
);

...

return deferred.promise;

由于我的其他函数都基于 Promise,因此我让这段代码返回一个 Promise 对象,以便可以轻松地将其包含在 then() 链中。

我真的需要两者吗?

我难以表达的最终问题是:在上面的代码示例中,我真的需要异步 Q吗?我正在学习如何用 Q 风格的承诺链替换异步模块的控制流......但它还没有“点击”我如何使用基于承诺的方法进行集合的映射或连接。或者,我想了解您为什么不能,或者为什么这不是一个好主意。

如果我在上面的示例中使用 async 和 Q 是为了一起工作,那么就这样吧。但如果我可以单独使用 Q,我宁愿不需要额外的库依赖。

对不起,如果我遗漏了一些非常明显的东西。异步事件驱动模型是一个非常不同的世界,我的脑袋还在游泳。

【问题讨论】:

您能否让您的示例真正异步?目前你只是在做一个pluck 嗯...正如我所说,我在这方面还很陌生。我还没有使用过下划线,但“pluck”的文档表明它是“map”最常见用途之一的简写。当我使用异步模块的“map”或“concat”函数时,它们不会让我阻塞并等待结果在原始范围内返回。相反,它们让我传递一个将在某个不确定点执行的回调。而且,呃……模块的名字是“async”!所以我很确定这是一个异步操作,即使它不是一个特别有趣或奇特的操作。 当然,我刚刚评论了您在上面发布的示例代码,您在async.concat 的回调函数中调用了callback 同步。如果你把它换成一些异步示例函数会更好(不给出实现) 这是 Caolan McMahon 的异步模块使用的编码风格。我不确定它是否实际上是同步的(尽管如果是的话肯定会很奇怪/具有讽刺意味)。 intention 是为了异步调用回调... :) @StevePerkins 我只想补充一点,Bluebird 库开箱即用地做到了这一点。此外,它更快。快了 100 倍。 【参考方案1】:

我真的需要两者吗?

没有。使用 Promise 在集合上映射异步迭代器非常简单,但它需要两个步骤而不是一个函数调用。首先,集合是mapped,用于并行迭代的一组承诺。然后,这些承诺被送入Q.all 为映射集合做出一个承诺。与async相比,结果的顺序是有保证的。

var entries = […]; // some array of objects with "id" attributes

var promises = entries.map(function(object) 
    return asyncPromiseReturingFunction(object);
); // the anonymous wrapper might be omitted
return Q.all(promises);

对于concat,您必须附加一个

.then(function(results) 
     return Array.prototype.concat.apply([], results);
);

【讨论】:

有趣!这很愚蠢,但我什至不熟悉核心 JavaScript 级别的“map”(而不是使用外部“async”模块)。到目前为止,我的大部分 JavaScript 开发都是在浏览器中进行的,而且我习惯于使用 jQuery 来尽可能多地避免特定于浏览器的问题。使用 Node,可以确定 VM 支持和不支持哪些功能! 基本上为了我的直接目的,我可以只使用核心 JavaScript“map()”来同步执行 pluck 操作(数组大小会非常小)......并且避免复杂化的需要周围的承诺链更进一步。 @StevePerkins:当然,如果您只想执行 pluck 操作,您既不需要承诺也不需要 async :-) 嘿。没错,但这里的代码 sn-p 存在于then() 承诺链的更大上下文中。这基本上是我的问题的要点......因为我使用 Q 来管理比更大的流,我可以通过使用 Q 来管理更大的流中的这个特定操作来避免额外的模块依赖吗?然而,事实证明,这个特定的操作根本不需要任何异步管理......使最初的问题对我的直接目的没有实际意义。对于更复杂的情况,您的示例仍然会派上用场。谢谢! 另外,Bluebird 代替 Q 支持 join() 功能。

以上是关于是否有一种纯粹的基于 Promise 的映射/连接集合的方法?的主要内容,如果未能解决你的问题,请参考以下文章

React 帮助:在映射数组中使用 Promise 进行条件渲染 [关闭]

是否有一种全球方式来更新所有基于位置的应用程序的位置?

是否可以纯粹基于自动布局来定义 UITableView 的 tableHeaderView 的高度?

TypeScript 中是不是有一种编译时方法来声明一个 keyof 类型,只有映射到特定类型的键? [复制]

如何在promise中简化这个有角度的重复代码?

没有自定义 SQL 的 Dapper 中的多重映射