Erick
Erick

Reputation: 833

JavaScript callback function when working withing a loop

This is what the code below does:

In my code, I am using setTimeout in a few occasions which I don't like. Instead of using setTimeout, I would like to properly use callback functions in a more efficient way (This might be the cause of my problem) What is the best way of me doing that?

$(document).ready(function() {


    $.ajax({ 

        url: 'getSearchSon.php',  

        type: 'POST',

        async: true,

        dataType: 'Text',

        /*data: { }, */

        error: function(a, b, c) { alert(a+b+c); }  

    }).done(function(data) {


    if(data != "connection")
    {
        var dataSent = data.split("|");

        var search_criterias = JSON.parse(dataSent[0]);

        var date_length = dataSent[1];

        var divison_factor = dataSent[2];

        var length = search_criterias.length;

        var arrXhr = [];

        var totalResultsArr = [];

        var helperFunc = function(arrayIndex)
        {
            return function()
            {
                var totalResults = 0;

                if (arrXhr[arrayIndex].readyState === 4 && arrXhr[arrayIndex].status == 200) 
                {
                    totalResults = JSON.parse(arrXhr[arrayIndex].responseText).queries.nextPage[0].totalResults;

                    totalResultsArr.push(totalResults);
                }
            }
        }

        var searchCriteriasFuc = function getTotalResults(searchParam, callback) 
        {   
            var searchParamLength = searchParam.length;

            var url = "";

            for(var i=0;i<searchParamLength;i++)
            {
                url = "https://www.googleapis.com/customsearch/v1?q=" + searchParam[i] + "&cx=005894674626506192190:j1zrf-as6vg&key=AIzaSyCanPMUPsyt3mXQd2GOhMZgD4l472jcDNM&dateRestrict=" + date_length;

                arrXhr[i] = new XMLHttpRequest();

                arrXhr[i].open("GET", url, true);

                arrXhr[i].send();

                arrXhr[i].onreadystatechange = helperFunc(i);
            }

            setTimeout(function()
            {       
                if (typeof callback == "function")  callback.apply(totalResultsArr);
            }, 4000);


            return searchParam;
        }   

        function callbackFunction()
        { 
            var results_arr = this.sort();

            var countResultsArr = JSON.stringify(results_arr);

            $.ajax({

                url: 'updateSearchDb.php',  

                type: 'POST',

                async: true,

                dataType: 'Text',

                data: { 'countResultsArr': countResultsArr },

                error: function(a, b, c) { alert(a+b+c); }  

            }).done(function(data) {

                var resultsDiv = document.getElementById("search");

                if(data == "NORECORD") resultsDiv.innerHTML = 'Updated failed. There was a problem with the database';

                else resultsDiv.innerHTML = 'Update was successful';

            }); //end second ajax call
        }

        //llamando funcion principal
        var arrSearchCriterias = searchCriteriasFuc(search_criterias, callbackFunction);

    }
    else
    {
        alert("Problem with MySQL connection.");
    }

    }); // end ajax 

});

Upvotes: 1

Views: 87

Answers (3)

Tamas Hegedus
Tamas Hegedus

Reputation: 29956

How you did it in 2015

Callbacks are things of the past. Nowadays you represent result values of asynchronous tasks with Promises. Here is some untested code:

$(document).ready(function() {
  $.ajax({ 
      url: 'getSearchSon.php',  
      type: 'POST',
      async: true,
      dataType: 'text'
      /*data: { }, */
  }).then(function(data) {
    if (data == 'connection') {
      alert("Problem with MySQL connection.");
    } else {
      var dataSent = data.split("|");
      var search_criterias = JSON.parse(dataSent[0]);
      var date_length = dataSent[1];
      var divison_factor = dataSent[2];

      return Promise.all(search_criterias.map(function(criteria) {
        return $.ajax({
          url: "https://www.googleapis.com/customsearch/v1"
            + "?q=" + criteria 
            + "&cx=005894674626506192190:j1zrf-as6vg"
            + "&key=AIzaSyCanPMUPsyt3mXQd2GOhMZgD4l472jcDNM"
            + "&dateRestrict=" + date_length,
          type: 'GET'
        });
      })).then(function(totalResultsArr) {
        totalResultsArr.sort();
        var countResultsArr = JSON.stringify(totalResultsArr);

        return $.ajax({
          url: 'updateSearchDb.php',  
          type: 'POST',
          async: true,
          dataType: 'text',
          data: { 'countResultsArr': countResultsArr },
          error: function(a, b, c) { alert(a+b+c); }  
        });
      }).then(function(data) {
        var resultsDiv = document.getElementById("search");
        if(data == "NORECORD") {
          resultsDiv.innerHTML = 'Updated failed. There was a problem with the database';
        } else {
          resultsDiv.innerHTML = 'Update was successful';
        }
      });
    }
  }).then(null, function() {
    alert('Some unexpected error occured: ' + e);
  });
});

This is how you do it in 2016 (ES7)

You can just use async/await.

$(document).ready(async() => {
  try {
    var data = await $.ajax({ 
        url: 'getSearchSon.php',  
        type: 'POST',
        async: true,
        dataType: 'text'
        /*data: { }, */
    });
    if (data == 'connection') {
      alert("Problem with MySQL connection.");
    } else {
      var dataSent = data.split("|");
      var search_criterias = JSON.parse(dataSent[0]);
      var date_length = dataSent[1];
      var divison_factor = dataSent[2];

      var totalResultsArr = await Promise.all(
        search_criterias.map(criteria => $.ajax({
          url: "https://www.googleapis.com/customsearch/v1"
          + "?q=" + criteria 
          + "&cx=005894674626506192190:j1zrf-as6vg"
          + "&key=AIzaSyCanPMUPsyt3mXQd2GOhMZgD4l472jcDNM"
          + "&dateRestrict=" + date_length,
          type: 'GET'
        }))
      );

      totalResultsArr.sort();
      var countResultsArr = JSON.stringify(totalResultsArr);
      var data2 = await $.ajax({
          url: 'updateSearchDb.php',  
          type: 'POST',
          async: true,
          dataType: 'text',
          data: { 'countResultsArr': countResultsArr },
          error: function(a, b, c) { alert(a+b+c); }  
      });
      if(data2 == "NORECORD") {
        resultsDiv.innerHTML = 'Updated failed. There was a problem with the database';
      } else {
        resultsDiv.innerHTML = 'Update was successful';
      }
    }
  } catch(e) {
    alert('Some unexpected error occured: ' + e);
  }
});

UPDATE 2016

Unfortunately the async/await proposal didn't make it to the ES7 specification ultimately, so it is still non-standard.

Upvotes: 2

Federico Baron
Federico Baron

Reputation: 997

To get the callback execute after google calls are finished you could change:

    var requestCounter = 0;        

    var helperFunc = function(arrayIndex)
    {
        return function()
        {
            if (arrXhr[arrayIndex].readyState === 4 && arrXhr[arrayIndex].status == 200) 
            {
                requestCounter++;                    

                totalResults = JSON.parse(arrXhr[arrayIndex].responseText).queries.nextPage[0].totalResults;

                totalResultsArr.push(totalResults);

                if (requestCounter === search_criterias.length) {
                    callbackFunction.apply(totalResultsArr);
                }
            }
        }
    }

then remove the setTimeout on searchCreteriaFuc.

Consider using promises and Promise.all to get all much cleaner :D

Upvotes: 0

Icepickle
Icepickle

Reputation: 12806

You could reformat your getTotalResults function in the following matter, it would then search rather sequential, but it should also do the trick in returning your results with an extra callback.

'use strict';

function getTotalResults(searchParam, callback) {
  var url = "https://www.googleapis.com/customsearch/v1?q={param}&cx=005894674626506192190:j1zrf-as6vg&key=AIzaSyCanPMUPsyt3mXQd2GOhMZgD4l472jcDNM&dateRestrict=" + (new Date()).getTime(),
    i = 0,
    len = searchParam.length,
    results = [],
    req, nextRequest = function() {
      console.log('received results for "' + searchParam[i] + '"');
      if (++i < len) {
        completeRequest(url.replace('{param}', searchParam[i]), results, nextRequest);
      } else {
        callback(results);
      }
    };
  
  completeRequest(url.replace('{param}', searchParam[0]), results, nextRequest);
}

function completeRequest(url, resultArr, completedCallback) {
  var req = new XMLHttpRequest();

  req.open("GET", url, true);
  req.onreadystatechange = function() {
    if (this.readyState === 4 && this.status == 200) {
      var totalResults = JSON.parse(this.responseText).queries.nextPage[0].totalResults;
      resultArr.push(totalResults);
      completedCallback();
    }
  };
  req.send();
}

getTotalResults(['ford', 'volkswagen', 'citroen', 'renault', 'chrysler', 'dacia'], function(searchResults) {
  console.log(searchResults.length + ' results found!', searchResults);
});

However, since you already use JQuery in your code, you could also construct all the requests, and then use the JQuery.when functionality, as explained in this question

Wait until all jQuery Ajax requests are done?

Upvotes: 0

Related Questions