How can you map the results of async calls in a generator, concurrently?
var generator = (function *() {
var lotsOfThings = yield asyncTask.call(generator);
var mappedResults = yield lotsOfThings.map(thing => {
// fails with a syntax error unless you do a `for…of` loop, but also doesn’t run concurrently regardless
return (yield asyncTask.call(generator, thing));
});
// then do something with mappedResults
})();
generator.next();
function asyncTask(…query) {
somethingAsync(…query, (err, res) => this.next(res));
}
Also, even in a regular for...of
loop, you can't run each asyncTask
concurrently. yield
will cause a pause between each task, essentially making a synchronous AJAX request. Ideally you'd want it to work like it does with promises, like this paradigm:
// these tasks will run concurrently (unlike the above example)
let promises = someThings.map(thing => {
return new Promise((resolve, reject) => {
somethingAsync((err, res) => {
resolve(res);
});
});
});
Promise.all(promises).then(/* do stuff */);
The promise approach can get hairy 'cause of all the nesting, but the benefit is that the async tasks can run concurrently… whereas the generators look nice, but looping through tasks is not concurrent. Any ideas?
I tried to sketch something similar without third-party libraries:
// Async runner
function async(generator){
var process = function(result){
if (result.done) {
return;
}
(Array.isArray(result.value) ? Promise.all(result.value) : result.value).then(function(value){
process(sequence.next(value));
});
};
var sequence = generator();
var next = sequence.next();
process(next);
};
// Generator function
var generator = function* () {
var list = yield getList();
console.log(list); // outputs [1, 2, 3, 4, 5]
var details = yield list.map(p => getDetails(p));
console.log(details); // outputs [11, 12, 13, 14, 15]
}
// Your async requests go here
function fakeAsync(f) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
f(resolve);
}, 500);
});
}
function getList() {
return fakeAsync(function(resolve) {
resolve([1, 2, 3, 4, 5]);
});
}
function getDetails(i) {
return fakeAsync(function(resolve) {
resolve(i + 10);
});
}
async(generator);
Is it what you were trying to achieve?
The answer I was looking for can be implemented with co .
co(function* () {
// maybe we need a token from our API before we can do anything
let token = yield new Promise((resolve, reject) {
getToken(token => resolve(token));
});
// these run in parallel
var queries = yield [
request('/route', token),
request('/another-route', token)
];
// [[], []] becomes [...]
return _.flatten(queries);
});
// our async request function returns a promise
function request(route, token) {
return new Promise((resolve, reject) => {
somethingAsync(route, token, (res) => {
resolve(res);
});
});
}
edit : changed somethingAsync
to not be then
able, as in my actual case, it's a call to a 3rd party API that does not already return a promise.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.