How to make a LOOP run according to the return of a Promise (promise)?

Asked

Viewed 1,025 times

3

How can I make my loop wait for the return of a Promise?

Let’s say I have the following code:

for (var i = 0; i < 10; i ++) {
   // algumalógica
   fazAlgumaCoisa(array[i]);
}

var fazAlgumaCoisa = function(obj) {
   $http.get(url).then(function success(response) {

   }, function error(response) {

   });
}

My question is: how do I wait for the return of this call http?

  • Can you explain in what context you want to use this feature? Without knowing what you want to do we’ll be guessing a solution that may not be useful.

  • You could specify that this is using an http script: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise#Creating_a_promise_2

  • I have a videoplayer where the configuration of what will play is described in several files. I need, before touching, to ensure that all the files have been read in memory. To get the file, I have to access a WS that returns me this.

4 answers

8


I put an answer with an idea that no one has mentioned yet, and that I think is the best option. Using Promise.all.

When you describe you need "before touching, ensure that all files have been read in memory." That’s exactly what the Promise.all ago.

Passing an array of Promises to that Promise. she hopes that all are solved and returns a Promise which receives as argument of the success method an array with the values of the past Promises. If you pass static values (Strings, Objects, or other variables that are not asynchronous) it uses it in the same, calling the Promise.resolve immediately. That is why Promise.all accepts a mixed array, with Promises or not.

A simple example would be like this: https://jsfiddle.net/4s5L4s7c/, what you need in your case would be something like this:

var urls = ['/pasta/a', '/pasta/b' /* etc... */ ];

function get(url, i) {
    return $http.get(url); <-- isto será o que queres
}

Promise.all(urls.map(get)).then(function(res) {
    console.log(res); // aqui "res" é uma array com a resposta de cada "$http.get", na mesma ordem da array "urls".
}, function() {
    console.log('Algo falhou!');
});
  • 1

    I think this is the best solution, it is simple, uses resources of the language itself and has something that has not been mentioned that it has advantage compared to the other answers: here we have several requests in parallel, using the real advantage of the JS asymchronism in its favor. Other solutions (which are also great) make one request at a time.

  • Sergio, inside this Promise.all(), where you’re passing a.map(get) urls, I could pass hand-created files?

  • @Exact paulogustavo. The . all method accepts an array of Promises or values/objects.

  • 1

    I didn’t know it, I found it very interesting and functional!!

5

You can use the concept of queues (queues).

Prepare a collection containing the items you want to process, and perform individual processing until the queue runs out.

One of the advantages of this pattern is that it allows you to include new items in the queue even while running the main loop.

Below is an example of FIFO serial queue (First In, First Out - or get in first, get out first):

inserir a descrição da imagem aqui

var app = angular.module("exemplo", []); 

app.controller("filaController", function($scope, $http) {

  var that = this;
  
  // Itens iniciais a serem processados
  this.ZipCodesAPesquisar = [77379, 77380, 77381];

  this.processaProximaEntrada = function(){
    //Existem itens na minha fila? Se sim, processe o primeiro.
    if (that.ZipCodesAPesquisar.length > 0){
      that.processaFila(that.ZipCodesAPesquisar[0]);
      return;
    }
  }

  this.processaFila = function (unidade){

    this.removeDaFila = function(unidade){
      //Localiza a posição da unidade na fila
      var indice = that.ZipCodesAPesquisar.indexOf(unidade);

      //Caso encontrado, remova:
      if (indice != -1) {
        that.ZipCodesAPesquisar.splice(indice, 1);
        
        // Verifique se ainda existem itens na fila.
        that.processaProximaEntrada();
      }
    }

    console.log("Processando " + unidade);

    $http.get('http://maps.googleapis.com/maps/api/geocode/json?address=' + unidade + '&sensor=true')
    .then(function success(response) {
      console.log(response.data.results[0].formatted_address);
      that.removeDaFila(unidade);
    }, function error(response) {
      that.removeDaFila(unidade);
    });    
  };

  //Inicializa processamento da fila
  this.processaProximaEntrada();

})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="exemplo" ng-controller="filaController">
</div>

  • Cute. That queue thing I liked :D

3

You can work with recursion. You use an anonymous named function to reference itself and, if it is within the conditions, you run it again. The set condition avoids infinite recursion.

var exemplo = function next(i) {


    $http.get(url).then(function success(response) {

        next.loop >=  i && next(++i);

    }, function error(response) {

        next.loop >= i && next(++i);
    });
}

exemplo.loop = 10;

exemplo(0);

In the above case, while the function property loop is greater than i, recursion will be applied. The anonymous function named internally from next, will be executed only when the success or error is called by $http.get.

I made a small example for you to understand how this works. It’s a function that writes a value on HTML. She will write while the value of loop is greater than that of i. It is the same case above, but applied with a setTimeout for you to understand the logic of my recursion:

var counter = function _fn(i) {

    _fn.loop > i && setTimeout(function () { 
    	_fn(++i) 
         document.body.innerHTML = i;
    }, 1000);
}

counter.loop = 10;

counter(0)

  • I would just like to understand why -1 does, since the answer causes the loop to run only when the previous request ends. I really don’t understand

  • For me, the answer is great. I’m just testing in my case to see how it will behave :)

  • I find the answer interesting but instead of iterating on numbers it was good to receive an array with files or url for those files and then map everything to the final callback that runs when those files have solved..

  • @Sergio now understood. Maybe the problem is that the answer was given on top of a question that doesn’t have much detail.

3

Similar to Reply from @Wallacemaxters, but in practice with the script you are using.

function loopPromise(url, start, end, callSuccess, callError){

    $http = new $http(url); // Gera a promise para a url solicitada 

    var func = function(obj){
        $http.get().then(function success(response){
            if(typeof callSuccess == 'function'){
                callSuccess('loaded'+start);  // chama sua função de sucesso, caso tenha sido definida
            }
            if(start++ < end){
                func(); // gera o loop ate start ser = a end
            }
        }, function(response){
            if(typeof callError == 'function'){
                callError(response); // chama sua função de erro, caso tenha sido definida, e não propaga mais o loop
            }
        });
    }
    func();
}

url = 'https://code.jquery.com/jquery-3.0.0.js';
loopPromise(url, 0, 10, function(response){
    console.log(response);
}, function(response){
    console.log(response);
});

Browser other questions tagged

You are not signed in. Login or sign up in order to post.