JavaEar 专注于收集分享传播有价值的技术资料

从生成器内部使用的迭代器回调中产生 (yielding from an iterator callback used inside a generator)

Has anyone tried to get Underscore JS or lodash (or any ES5 standard functions for that matter) working with generators?

If we have an array var myArray = [1,2,3,4,6]; We want to forEach over it.

In a non generator case you would simply

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

However, when you can't yield inside a non generator function, so if inside this loop we had to do some async work, you would need to do the following.

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

Which kind of sucks.

Anyone know of a way to get anonymous functions working with generators? We kind of lose the entire lodash library because of this.

Note: I'm using Traceur to compile my code into ES6 with generators turned on.
Note: I'm not using co(). I'm using a custom generator function seen below

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

1个回答

    最佳答案

  1. 英文原文

    If I'm understanding your problem correctly, it's essentially that you're trying to do something (iterate until a good stopping point is found) in an asynchronous way, in a language (JS) which is really designed around synchronicity. In other words, while you could normally do:

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

    you instead want to make the "should we stop looping" code break from normal execution, and then come back, which isn't possible with traditional JS (yield is relatively new to the language) and thus isn't possible in Underscore/Lodash:

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

    There are two approaches you could take, neither of which are ideal.

    As mentioned in the comments, one approach would be to do all your "deferred" work first, then iterate:

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

    But (as also noted in the comments) that isn't quite the same, as you wind up doing the AJAX work on all of the elements of your array (vs. a true generator which would only iterate through as many as needed to find a "winner").

    Another approach would be to eliminate the asynchronicity. jQuery allows you to pass async: false to an AJAX request, which "solves" the problem by letting you use Underscore/Lodash/whatever ... but it also locks your user's browser up for as long as it takes to do the AJAX work, which probably isn't what you want.

    Unfortunately if you want to use a library like Underscore/Lodash those are the only options I can see. Your only other option would be to write your own Underscore/Lodash mix-in, which really isn't that hard. I'd recommend doing this, as it would allow you still leverage all the other great functions in those libraries while still iterating in a consistent way.


    中文翻译

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

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

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

      _([1,2,3])。any(function(x){
        var shouldWeStopLooping = $ .ajax(...); //不起作用;代码继续
        returnWeWeStopLogging;
    });
     

    您可以采取两种方法,但这两种方法都不理想。

    正如评论中所提到的,一种方法是首先完成所有" 延期" 工作,然后迭代:

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

    但是(正如评论中所指出的那样)并不完全相同,因为你最终会对数组的所有元素进行AJAX工作(相对于真正的生成器,它只会迭代尽可能多的需要找到" 胜利者" )。

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

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

    If I'm understanding your problem correctly, it's essentially that you're trying to do something (iterate until a good stopping point is found) in an asynchronous way, in a language (JS) which is really designed around synchronicity. In other words, while you could normally do:

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

    you instead want to make the "should we stop looping" code break from normal execution, and then come back, which isn't possible with traditional JS (yield is relatively new to the language) and thus isn't possible in Underscore/Lodash:

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

    There are two approaches you could take, neither of which are ideal.

    As mentioned in the comments, one approach would be to do all your "deferred" work first, then iterate:

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

    But (as also noted in the comments) that isn't quite the same, as you wind up doing the AJAX work on all of the elements of your array (vs. a true generator which would only iterate through as many as needed to find a "winner").

    Another approach would be to eliminate the asynchronicity. jQuery allows you to pass async: false to an AJAX request, which "solves" the problem by letting you use Underscore/Lodash/whatever ... but it also locks your user's browser up for as long as it takes to do the AJAX work, which probably isn't what you want.

    Unfortunately if you want to use a library like Underscore/Lodash those are the only options I can see. Your only other option would be to write your own Underscore/Lodash mix-in, which really isn't that hard. I'd recommend doing this, as it would allow you still leverage all the other great functions in those libraries while still iterating in a consistent way.

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

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

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

      _([1,2,3])。any(function(x){
        var shouldWeStopLooping = $ .ajax(...); //不起作用;代码继续
        returnWeWeStopLogging;
    });
     

    您可以采取两种方法,但这两种方法都不理想。

    正如评论中所提到的,一种方法是首先完成所有" 延期" 工作,然后迭代:

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

    但是(正如评论中所指出的那样)并不完全相同,因为你最终会对数组的所有元素进行AJAX工作(相对于真正的生成器,它只会迭代尽可能多的需要找到" 胜利者" )。

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

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