Execute $ .ajax within a cycle

4

I have a problem with the following code, I want to execute within a cycle for a call $.ajax until the exit condition of the cycle is fulfilled, however the cycle does all its iterations and in the final iteration run the ajax , so I can not get the effect I need in my algorithm.

var streamers = ["qt314g", "feelinkgamer", "brunofin", "streamerhouse"];
var urljoin = "";

for (k in streamers){
  urljoin = "https://api.twitch.tv/kraken/streams/" + streamers[k] + "?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z";
  $.ajax({
    type: 'GET',
    url: urljoin,
    dataType: "jsonp",
    jsonpCallback: "twitchcallback"
  });
}

function twitchcallback(json) {
  alert(json["_links"]["self"]);
  if(k>=streamers.length-1){
     alert("Finished");
  }
}
    
asked by Ricardo Lira 05.01.2017 в 21:33
source

5 answers

4

This is a very recurring problem that I notice that developers have, Ajax calls like other I / O operations are asynchronous operations in javascript, you can read other questions related to this topic, a way to deal with the flow of execution of Asynchronous operations is with the use of promises.

Using Promises.all you can run a stack of promises and get the result when all have been executed, then.

var streamers = ["qt314g", "feelinkgamer", "brunofin", "streamerhouse"];
var urljoin = "";
var requests = []; // Pila de solicitudes

for (k in streamers){
  urljoin = "https://api.twitch.tv/kraken/streams/" + streamers[k] + "?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z";
  requests.push(
	$.ajax({
      type: 'GET',
      url: urljoin,
      dataType: "jsonp"
    })
  )
}

Promise.all(requests).then(streamersValues => { 
  streamersValues.forEach( streamer => console.log(streamer["_links"]["self"]));
  alert('Terminado!')
});

More concise

var streamers = ["qt314g", "feelinkgamer", "brunofin", "streamerhouse"];
var urljoin = "";

var requests = streamers.map(streamer => {
  urljoin = "https://api.twitch.tv/kraken/streams/" + streamer + "?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z";
  return (
    $.ajax({
      type: 'GET',
      url: urljoin,
      dataType: "jsonp"
    })
  )
})

Promise.all(requests).then(streamersValues => { 
  streamersValues.forEach( streamer => console.log(streamer["_links"]["self"]));
  alert('Terminado!')
});

Without using the arrow operator

var streamers = ["qt314g", "feelinkgamer", "brunofin", "streamerhouse"];
var urljoin = "";

var requests = streamers.map(function(streamer) {
  urljoin = "https://api.twitch.tv/kraken/streams/" + streamer + "?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z";
  return (
    $.ajax({
      type: 'GET',
      url: urljoin,
      dataType: "jsonp"
    })
  )
})

Promise.all(requests).then(function(streamersValues) { 
  streamersValues.forEach( function(streamer) {
    console.log(streamer["_links"]["self"])
  });

  alert('Terminado!')
});
    
answered by 05.01.2017 в 22:20
0

You can try to create a function to make your call AJAX and in this way have a little more "independence" at the time of execution:

var streamers = ["qt314g", "feelinkgamer", "brunofin", "streamerhouse"];
var urljoin = "";

for (k in streamers){
    urljoin = "https://api.twitch.tv/kraken/streams/" + streamers[k] + "?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z";
    myAjaxCall(urljoin);
}

function myAjaxCall(urljoin){
    $.ajax({
        type: 'GET',
        url: urljoin,
        dataType: "jsonp",
        jsonpCallback: "twitchcallback"
    });
}

function twitchcallback(json) {
    alert(json["_links"]["self"]);
    if(k >= streamers.length-1){
        alert("Finished");
    }
}

If you are working with jQuery the simplest and most recommendable would be to use $.each in the following way:

var streamers = ["qt314g", "feelinkgamer", "brunofin", "streamerhouse"];
var urljoin = "";

$.each(streamers, function(index, streamer){
    urljoin = "https://api.twitch.tv/kraken/streams/" + streamer + "?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z";
    $.ajax({
        type: 'GET',
        url: urljoin,
        dataType: "jsonp",
        jsonpCallback: "twitchcallback"
    });
}

function twitchcallback(json) {
    alert(json["_links"]["self"]);
    if(k >= streamers.length-1){
        alert("Finished");
    }
}
    
answered by 05.01.2017 в 21:50
0
  

I have a problem with the following code, I want to run within a cycle for a call $ .ajax until the exit condition of the cycle is fulfilled , however the cycle does all its iterations and in the final iteration execute the ajax.

According to what I understand, your objective is that for each iteration execute an ajax request. And what you get, is that your code makes the request in the last iteration. Before continuing, the following is important:

  

JavaScript is an asynchronous language, which means that there is no fixed execution order that you can trust. Not all language features are blocking, there are some that do, but the vast majority do not. as is the case with ajax requests.

You will always find that you are running processes in parallel and you do not know how to act. In ES5, it was very common to see this type of programmer errors that came from non-functional languages. With the arrival of ES6 you solve much of this problem with the help of Promises , block scopes and in the next version, Async/Await .

ES5

The solution before ES6 + was to use a closure inside the loop to create a scope for the control variable:

for (var s of streamers) {
  (function(s) {
    $.ajax({
      url: 'https://api.twitch.tv/kraken/streams/${s}?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z',
      type: 'GET',
      dataType: 'jsonp',
      jsonCallback: 'twitchcallback'
    })
    .done(data => {
      console.log('Streamer ${s} complete');
      console.log(data);

      if (s === streamers[streamers.length - 1]) {
        alert('finished');
      }
    });
  })(s);
}

Demo

ES6

Solving it in ES6 is much simpler. It is enough to use a local scope instead of a global one as it is var :

for (let s of streamers) {
  $.ajax({
    url: 'https://api.twitch.tv/kraken/streams/${s}?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z',
    type: 'GET',
    dataType: 'jsonp',
    jsonCallback: 'twitchcallback'
  })
  .done(data => {
    console.log('Streamer ${s} complete');
    console.log(data);

    if (s === streamers[streamers.length - 1]) {
      alert('finished');
    }
  });
}

Demo

ES Next: Async / Await

With async await you can control the flow much more naturally if you come from non-functional languages. The code written with async/await is still asynchronous even if we write it synchronously.

  

Note: To use await you must be in a function marked with async

(async () => {
    for (let s of streamers) {
    const data = await (
      $.ajax({
        url: 'https://api.twitch.tv/kraken/streams/${s}?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z',
        type: 'GET',
        dataType: 'jsonp',
        jsonCallback: 'twitchcallback'
      })
    );
    console.log('Streamer ${s} complete');
    console.log(data);
    if (s === streamers[streamers.length - 1]) {
      alert('finished');
    }
  }
})();

Demo

If you decide to use the third form you should use babel :

babel archivo.js destino.js
    
answered by 06.01.2017 в 00:55
0

If you want to do it in series until you find a streamer with some particular content or structure, it's something like this:

var streamers = ["qt314g", "feelinkgamer", "brunofin", "streamerhouse"];
function requestStreamers(streamers, callback){
    var urljoin = "";
    var current = 0;
    streamers = streamers || [];
    function nextRequest(){
        if(streamers[current]){
            urljoin = "https://api.twitch.tv/kraken/streams/" + streamers[current] + "?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z";
            $.ajax({
              type: 'GET',
              url: urljoin,
              dataType: "jsonp",
              jsonpCallback: function(json){
                  if(true /*condición que debe cumplir*/){
                      callback(null, json);
                  } else {
                      nextRequest();
                  }
              }
            });
        } else {
            callback(new Error("No useful stream found"));
        }
    }
}

But if you just want to make the request to everyone, two things happen, k will always be 3 in your example, by the time the first callback arrives, the for has already finished, the second is that you can not guarantee that the last request is the last one to answer, it is possible that by server topics, the first one responds to the end, or half, etc, so the best solution is this:

var streamers = ["qt314g", "feelinkgamer", "brunofin", "streamerhouse"];
var urljoin = "";
var requestsFinished = 0;

for (k in streamers){
    urljoin = "https://api.twitch.tv/kraken/streams/" + streamers[k] + "?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z";
    $.ajax({
      type: 'GET',
      url: urljoin,
      dataType: "jsonp",
      jsonpCallback: "twitchcallback"
    });
}

function twitchcallback(json) {
  requestsFinished ++;
  alert(json["_links"]["self"]);
  if(requestsFinished >= streamers.length){
     alert("Finished");
  }
}
    
answered by 06.01.2017 в 00:57
0
var streamers = ["qt314g", "feelinkgamer", "brunofin", "streamerhouse"];
    var i;
    for (i = 0; i < streamers.length; i++) {
        $.ajax({
            url: "https://api.twitch.tv/kraken/streams/" + streamers[i] + "?client_id=1t2i1eidglzctkjlx1xckdtguuj0y2z",
            data:{},
            dataType: "jsonp",
            type:"GET",
            beforeSend: function(){
            },
            success: function(rs){
                //Tu código aquí
                alert(rs);
            },
            error: function(er1, er2, er3){
                alert(er1+" "+er2+" "+er3);
            }
        });

        if(i>=streamers.length){
            alert("Finished");
        }
    }
    
answered by 06.01.2017 в 00:30