Search code examples
javascriptjqueryreactjsflux

Need to make multiple Async calls before executing next step JS


I have an array that can hold an unknown amount of indexes in it. Each index is used to send data with an ajax call. I am looping through with a for loop gathering the data from the successful call and pushing it into an empty array. At the end of the unknown amount of calls I then need to use that newly gathered array in my view. newDataArray is executed at the bottom before the loops are done and therefor it is still empty. How do I finish all the calls then do what is at the bottom?

If it helps, I am doing this in React with the Flux pattern. But the same issue may be done not in React. Here is a mock sample of what I am trying to do:

JS

case 'execute-calls':

    //This is the new array to push to
    var newDataArray = [];
    //Url to call
    var url = 'http://dev.markitondemand.com/Api/v2/Quote/jsonp';

    for(let i = 0; i < payload.data.length; i++){

        //given array of data that needs to be sent with call
        let symb = { symbol: payload.data[i]};
        $.ajax({
          data: symb,
          url: url,
          dataType: "jsonp",
        })
          .done(function(data){
            let updatedData = {
              //...data that is stored from response
            };

            newDataArray.push(updatedData);
          })
          .fail(function(error){
            //console.log(error);
          });

      }

    //This will be updating the state object which is above the switch cases
    //However this is ran before the end of the loops so newDataArray is empty
    var updateTicker = {
        updatedTicker: true,
        updatedTickerSymbols: newDataArray
    };
    assign(stockData,updateTicker);
    getStockData.emitChange();

    break;

Solution

  • You can make use of the fact that $.ajax() actually returns a deferred object, and use it to create an array of deferreds. e.g.

    var symbols = [1, 2, 3, 4];
    
    var deferreds = symbols.map(function (symbol) {
      return $.ajax({
        url: 'http://dev.markitondemand.com/MODApis/Api/v2/Quote/jsonp',
        data: { symbol: symbol },
        dataType: 'jsonp'
      });
    });
    

    You can resolve multiple deferreds at once with $.when(). There is a complication however, $.when() expects a list of parameters rather than array. We can solve this by using Function#apply.

    To add to the complication, the callback function is also called with a list of arguments. Since we don't know how many arguments there are, we'll use the arguments pseudo-array. And since arguments isn't an actual array, we'll loop through it by using Function#call on Array#prototype.

    $.when.apply($, deferreds).done(function () {
      Array.prototype.forEach.call(arguments, function (response) {
        console.log(response[0].Message);
      });
    }).fail(function (jqXHR, textStatus, error) {
      console.error(error);
    });
    

    [UPDATED to include fail() call]

    If you're using ES6 this is much more elegant:

    $.when(...deferreds).done((...responses) => {
      responses.forEach((response) => {
        console.log(response[0].Message);
      });
    });