Spen Spen - 3 months ago 9
Javascript Question

Asynchronous call for every item of list with generator

I didn't know how to appropriately write the title of this question but i will explain my problem:

Imagine there is a list with some data, for example:

["first", "second", "third"]


Also there is a AJAX call that does something to it's argument, for example:

function ajax(data){
let d = new $.Deferred();
setTimeout(() => d.resolve(data+"1"), 1000);
return d.promise();
}


And for every AJAX call you need a follow-up action, for example:

ajax(e).done(data => d.resolve(data+"2"));


Now i want to make the AJAX call and the follow-up action asynchronously for every item of the list but want to wait (non blocking) until every item is finished.

For a solution i want to use generators and the co library.

Only running the AJAX call asynchronously for every list item works great:



var consoleLine = "<p class=\"console-line\"></p>";

console = {
log: function (text) {
$("#console-log").append($(consoleLine).html(text));
}
};

co(function*(){
let res = yield ["first", "second", "third"].map(e => ajax(e));
res.forEach((a, b, c) => console.log(a));
});

function ajax(data){
let d = new $.Deferred();
setTimeout(() => d.resolve(data+"1"), 1000);
return d.promise();
}

.console-line
{
font-family: monospace;
margin: 2px;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>

<div id="console-log"></div>





But running it with the follow-up action doesn't work:



var consoleLine = "<p class=\"console-line\"></p>";

console = {
log: function (text) {
$("#console-log").append($(consoleLine).html(text));
}
};

co(function*(){
let res = yield test(["first", "second", "third"]);
res.forEach((a, b, c) => console.log(a));
});

function test(g) {
return g.map(e => function(){
let d = new $.Deferred();
ajax(e).done(data => d.resolve(data+"2"));
return d.promise();
});
}

function ajax(data){
let d = new $.Deferred();
setTimeout(() => d.resolve(data+"1"), 1000);
return d.promise();
}

.console-line
{
font-family: monospace;
margin: 2px;
}

<script src="https://raw.githubusercontent.com/tj/co/master/index.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="console-log"></div>





Why is that? How to get my requirement to work?

Answer

Here's your problem:

return g.map(e => function(){
//             ^^ ^^^^^^^^^^
    let d = new $.Deferred();
    ajax(e).done(data => d.resolve(data+"2"));
    return d.promise();
});

That's an arrow function returning a function expression. You either want

return g.map(function(e) {

or

return g.map(e => {

to make it work, otherwise you only get back an array of functions (and co will treat that in weird ways).


And yes, you definitely should use then instead of done + deferreds.