A
A
Anton Dyshkant2015-03-14 00:52:50
JavaScript
Anton Dyshkant, 2015-03-14 00:52:50

How to force javascript to wait for ajax response?

Hello!

I've been banging my forehead against the wall for a couple of hours already :)

I have a database with a table that stores data about locations (fields id, location, start, end, description). The location column stores place_id from the Google Maps API , by which this API can find the location and put it on the map (with a marker).

According to the conditions of the problem, during one visit to the page, the number of records in the table can change (records can be changed, deleted or added). Therefore, it was decided that three different functions in javascript would be responsible for creating a map, creating a list of markers, and displaying these markers.

$(document).ready(function()
{
    // создает карту
    var map = create_map();
    
    // создает маркеры, не отображая их
    var markers = create_markers(map);
    
    // показывает, что объекты в массиве есть (об этом читайте далее)
    console.log(markers);
    
    // показывает 0 (об этом читайте далее)
    console.log(markers.length);
    
    // включает отображение маркеров
    setAllMap(map, markers);
});


Actually, creating a map is simple:
function create_map() {
    var mapOptions = {
        center: {lat: 0, lng: 0},
        zoom: 1
    };

    var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
    
    return map;
}


But the array in which the marker objects are located is obtained by accessing a php script that pulls and gives an array with data from the database to js. Further, this array is processed, and at the output we get the necessary marker objects.
function create_markers(map) {
    console.log("create_markers_start");
    
    var service = new google.maps.places.PlacesService(map);
    
    var infowindow = new google.maps.InfoWindow();
    
    var markers = [];
    
    $.ajax({
        async:      false,
        url:        '/index.php/locations/get_locations_list_json',
        success:    function(locations_json) {
            console.log("create_markers___ajax_success_start");
                        var locations = $.parseJSON(locations_json);
                        $.each(locations, function(i, location_point) {
                        
                            var request = {
                                placeId: location_point.location
                            };
                            
                            Date.prototype.getMonthName = function() {
                                var month = ['Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'];
                                return month[this.getMonth()];
                            }
                            var desc = location_point.description;
                            var start_obj = new Date(location_point.start);
                            var start = start_obj.getMonthName() + " " + start_obj.getDate();
                            var end_obj = new Date(location_point.end);
                            var end = end_obj.getMonthName() + " " + end_obj.getDate();
                            
                            service.getDetails(request, function(place, status) {
                                if (status == google.maps.places.PlacesServiceStatus.OK) {
                                    markers[i] = new google.maps.Marker({
                                        map: null,
                                        position: place.geometry.location
                                    });
                                    google.maps.event.addListener(markers[i], 'click', function() {
                                        infowindow.close();

                                        infowindow.setContent('<div><strong>' + place.name + '</strong><br>' +
                                        'You\'re going to visit <u>' + place.formatted_address + '</u>' +
                                        ' <b>from</b> ' + start +
                                        ' <b>to</b> ' + end +
                                        '<br><br>' + desc);
                                        infowindow.open(map, this);
                                    });
                                }
                            });
                        });
                    }
    });
    console.log("create_markers_end");
    return markers;
}


The problem is this: by the time setAllMap is launched , the markers array is empty (although there was a function above the text that was supposed to fill it.
Which is typical. As you can see, the console.logs are arranged in the text. The log is shown below (you can see the sequence of script execution, and also the output of markers and markers.length:
create_markers_start
create_markers___ajax_success_start
create_markers_end
var_markers
Array[7]
0

In Array[7] you can observe the desired marker objects. But, as you can see, at the same moment markers.length shows zero. I believe this is due to the peculiarities of logging. I also believe that this is due to the fact that the code uses an ajax call (which I made synchronous, but did not help).

How to solve the problem? How to make sure that by the time the setAllMap(map, markers) function is launched, the markers variable already contains the array we need?

Thank you.

Z.Y. For all this to work, the Google Maps library for JS is connected to the page .

---
UPDATE

Thanks Maxim , the issue with the launch order has been resolved. But the following problem appeared (or rather, was not resolved): markers.length is still equal to zero!
Here is the call code:
create_markers(map, function(markers) {
        console.log("in `create_markers`: markers: ");
        console.log(markers);
        console.log("in `create_markers`: markers.length: ");
        console.log(markers.length);
    
        // включает отображение маркеров
        setAllMap(map, markers);  
    });

Here is the function code:
function create_markers(map, fn) {
   var markers = {}
   $.ajax(..., {
       success: function(...) {
         ...
         console.log("markers: ");
         console.log(markers);
         console.log("markers.length: ");
         console.log(markers.length);
         fn(markers);
       }
   });
}

And here is the console:
markers: 
Array[7]
markers.length: 
0
in `create_markers`: markers: 
Array[7]
in `create_markers`: markers.length: 
0


---
UPDATE 2

Issue resolved as follows:
function create_markers(map, fn) {
    ...
    var markers_array = [];
    var promises_array = [];
    
    $.ajax({
        ...
        success: function(...)
        {
            $.each(..., function(...) {
                // отслеживаем состояние асинхронного запроса
                var dfd;
                dfd = new $.Deferred();

                // это асинхронный запрос, для его правильной обработки используем промисы
                service.getDetails(..., function(...) {
                    var marker = ...;
                    // по завершению формирования маркера отправляем его в массив
                    markers_array.push(marker);

                    // затем помечаем, что асинхронный запрос завершился успехом
                    dfd.resolve();
                });

                // отправляем отчет об успешном завершении очередного асинхронного запроса в массив с промисами
                promises_array.push(dfd.promise());
            });

            // если все асинхронные вызовы завершились - вызываем callback
            $.when.apply($, promises_array).then(function(){
                fn(markers_array);
            });
        }
    });
}


$(document).ready(function()
{
    ...

    create_markers(map, function(markers_array) {
        ...
    });
});

Answer the question

In order to leave comments, you need to log in

3 answer(s)
M
Max, 2015-03-14
@vyshkant

It is necessary to pass a callback to `create_markers`, which will be called after the data is loaded and the object with markers is filled.

function create_markers(map, fn) {
   var markers = {}
   $.ajax(..., {
       success: function(...) {
         ...
         fn(markers);
       }
   });
}

var map = create_map();
    
create_markers(map, function(markers) {
  // показывает, что объекты в массиве есть (об этом читайте далее)
  console.log(markers);

  // показывает 0 (об этом читайте далее)
  console.log(markers.length);

  // включает отображение маркеров
  setAllMap(map, markers);  
});

In your case, the object is empty, because the empty object function returns before the response from the server is received. Next, google about asynchrony in js.

I
Ilya Bobkov, 2015-03-14
@heksen

specify async: false in ajax, but the problem is that the browser gets up until the data is loaded, i.e. at best, callbacks should be used.

N
Natalia Bazenova, 2015-03-14
@logiciel

Judging by the code, the markers variable is defined twice:
var markers = create_markers(map);
and then in create_markers:
var markers = [];
This second one seems to be redundant.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question