【问题标题】:yielding from an iterator callback used inside a generator从生成器内部使用的迭代器回调中产生
【发布时间】:2014-09-17 23:48:29
【问题描述】:

有没有人尝试让 Underscore JS 或 lodash(或任何 ES5 标准函数)与生成器一起工作?

如果我们有一个数组var myArray = [1,2,3,4,6]; 我们希望 forEach 覆盖它。

在非生成器的情况下,您只需

myArray.forEach(function(k) {
  console.log(k);
});

但是,当你不能在非生成器函数中产生时,所以如果在这个循环中我们必须做一些异步工作,你需要做以下事情。

var foreach = function* (arr, fn) {
  var i;
  for (i = 0; i < arr.length; i++) {
    yield * fn(arr[i], i);
  }
};

yield* foreach(myArray, function* (k) {
  var a = yield fs.readFile();
});

哪一种很烂。

有人知道如何让匿名函数与生成器一起工作吗?因此,我们有点失去了整个 lodash 库。

注意:我正在使用 Traceur 将我的代码编译成 ES6 并打开生成器。
注意:我没有使用 co()。我正在使用如下所示的自定义生成器函数

var run = function(generatorFunction) {
  var generatorItr = generatorFunction(resume);
  function resume(callbackValue) {
    generatorItr.next(callbackValue);
  }
  generatorItr.next();
};

【问题讨论】:

  • 也许只有我一个人,但我不明白到底是什么问题。这听起来更像是问题正在使用例如forEach 带生成器。
  • 嗯,是的,但这不是真正的*问题。问题是在非生成器函数中使用 yield。哪个 ForEach 将使用 90% 的时间。更不用说 _.find()、_.filter()、Array.reduce()、Array.forEach()、Array.map()。如果你需要在里面产生任何东西,所有这些都是无用的。
  • 如果是forEach,您可以简单地使用for (var e of arr) { yield doSomethingWith(e); } 或普通的for 循环。对于filterreduce 等其他方法,我看不出使用生成器会有什么用处。 filter 回调必须返回一个布尔值。在这里使用生成器到底有什么意义?
  • 好吧,我能想到的任何答案都是低效的代码。在循环中做工作。但是无论如何,如果您有一个 mp3 url 列表,并且您需要将该列表过滤到文件系统中实际存在的列表。你通常会在你的列表上做一个过滤器,在每次迭代时检查 FS,并在它们都完成时检查 Promise.all()。使用生成器我们不能使用过滤器。我们必须循环并存储第二个结果数组。
  • @SeanClark:请注意,Promise.all 将并行启动 fs 查询,而您似乎正在寻找的生成器解决方案将是顺序的。

标签: javascript function generator ecmascript-6 yield-keyword


【解决方案1】:

如果我正确理解了您的问题,那么本质上是您尝试以一种异步方式,使用一种真正围绕同步性设计的语言 (JS) 做某事(迭代直到找到一个好的停止点)。换句话说,虽然你通常可以这样做:

_([1,2,3]).any(function(x) {
    var shouldWeStopLooping = x % 2 == 0;
    return shouldWeStopLogging;
});

您想要让“我们应该停止循环”代码从正常执行中中断,然后返回,这对于传统 JS 是不可能的(yield 对语言来说相对较新),因此不是可以在 Underscore/Lodash 中:

_([1,2,3]).any(function(x) {
    var shouldWeStopLooping = $.ajax(...); // Doesn't work; code keeps going
    return shouldWeStopLogging;
});

您可以采用两种方法,但都不是理想的。

如 cmets 中所述,一种方法是先完成所有“延迟”工作,然后进行迭代:

var workInProgress = _([1,2,3]).map(someAjaxOperation);
$.when.apply(workInProgress).done(doSomethingBasedOnAjaxResults);

但是(正如 cmets 中所指出的那样)并不完全相同,因为您最终会对数组的所有元素进行 AJAX 工作(与真正的生成器相比,它只会迭代尽可能多的需要找到一个“赢家”)。

另一种方法是消除异步性。 jQuery 允许您将async: false 传递给 AJAX 请求,这通过让您使用 Underscore/Lodash/whatever 来“解决”问题......但它也会锁定您用户的浏览器,只要它需要执行 AJAX 工作,这可能不是你想要的。

不幸的是,如果您想使用像 Underscore/Lodash 这样的库,我只能看到这些选项。您唯一的其他选择是编写自己的 Underscore/Lodash 混合,这真的不难。我建议这样做,因为它可以让您仍然利用这些库中的所有其他出色功能,同时仍然以一致的方式进行迭代。

【讨论】:

  • 不完全是。找到 x 时,我不需要停止循环。我只是想使用 lodash 循环功能。喜欢过滤和减少。或者 ES6 reduce 。在某些情况下,我希望事情发生在一个可以是异步的循环中,但我想要一种更好的方式来编写该代码。这就是整件事的重点,编写更好的代码。
  • 另外,我同意您的 jQuery async: false 是客户端上的解决方案,但这里也并非如此。我希望事情是异步的,因为这是 NodeJS。我只希望我编写的代码远离回调地狱。我想要一个使用循环的好解决方案,但现在看起来 yield 不能在闭包情况下使用,它必须在 gen 函数中。 ES7 似乎有异步函数——这可能是答案。
猜你喜欢
  • 2011-09-09
  • 1970-01-01
  • 2014-10-31
  • 2018-05-16
  • 2020-04-23
  • 2018-11-01
  • 2015-06-24
  • 1970-01-01
  • 2016-05-20
相关资源
最近更新 更多