admin管理员组文章数量:1434966
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 pile 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();
};
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 pile 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();
};
Share
Improve this question
edited Jul 26, 2014 at 19:25
Bergi
667k161 gold badges1k silver badges1.5k bronze badges
asked Jul 26, 2014 at 3:41
Sean ClarkSean Clark
1,4561 gold badge17 silver badges32 bronze badges
6
-
2
Maybe it's just me, but I don't understand what exactly the problem is. It sounds more like the problem is using e.g.
forEach
with generators. – Felix Kling Commented Jul 26, 2014 at 4:51 - 1 Well yes, but that's not the real* problem. The problem is using yield inside of a non generator function. Which ForEach will use 90% of the time. Not to mention _.find(), _.filter(), Array.reduce(), Array.forEach(), Array.map(). All of them are useless if you need to yield anything inside. – Sean Clark Commented Jul 26, 2014 at 17:07
-
4
In case of
forEach
, you can simply usefor (var e of arr) { yield doSomethingWith(e); }
or a normalfor
loop. For other methods such asfilter
orreduce
, I don't see how using a generator would even be useful. Thefilter
callback must return a boolean. How exactly would using a generator make sense here? – Felix Kling Commented Jul 26, 2014 at 18:25 - Well, any answer I can think of would be inefficient code. Doing work inside a loop. But anyway, if you have a list of mp3 urls and you need to filter that list down to ones that actually exist on the filesystem. You would normally do a filter on your list, check the FS on each iteration, and Promise.all() when they all are done. Using generators we can't use filter. We have to loop and store a 2nd array of the results. – Sean Clark Commented Jul 26, 2014 at 18:34
-
@SeanClark: Notice that
Promise.all
would have started the fs queries in parallel, while the generator solution you seem to be looking for would be sequential. – Bergi Commented Jul 26, 2014 at 19:29
2 Answers
Reset to default 1If 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 e 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 ments, 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 ments) 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 remend doing this, as it would allow you still leverage all the other great functions in those libraries while still iterating in a consistent way.
This works:
class Observable {
constructor(ids) {
this.generator =
async function *generator() {
let response;
let people;
for (const id of ids) {
response = await fetch(`https://swapi.dev/api/people/${id}`);
people = await response.json();
yield people.name;
}
};
}
subscribe(callback) {
(async function() {
for await (const i of this.generator()) {
callback(i);
}
}).bind(this)();
}
}
const observable = new Observable([1, 2, 3]);
observable.subscribe(console.log);
本文标签: javascriptyielding from an iterator callback used inside a generatorStack Overflow
版权声明:本文标题:javascript - yielding from an iterator callback used inside a generator - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745627254a2667049.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论