Tengo un código ajax como ese:

$.ajax({
    type: "post",
    async: false,
    url: "/FindAVet/Search",
    data: '{"vetname":"' + $("#VetName").val() + '","lat":"' + objlatitude + '","lng":"' + objlongitude + '","radius":"' + $("#hdnRadius").val() + '","searchAll":"' + searchAll + '"}',
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function (result) {
        if (result.IsValid == true) {
            $("#divMapContainer").show();
            $("#placeholderServiceagent").html('');
            $("#placeholderServiceagent").show();
            $("#placeholderServiceagent").html(result.Datavalue);
            $("#noserviceagentstatus").hide();

            var arrLoc = result.Locations.split(";");
            var arrInf = result.InfoWindowContents.split(";");

            var source, destination, distance;
            source = $("#SuburbOrPostcode").val();

            var service = new google.maps.DistanceMatrixService();

            var arr2dLoc = [];
            var arr2dInf = [];
            for (var i = 0; i < arrLoc.length; i++) {
                arr2dLoc[i] = arrLoc[i].split(",");
            }
            var j = 1;
            for (var i = 0; i < arrInf.length; i++) {

                arr2dInf[i] = arrInf[i].split(",");
                var strarr2dInf = arr2dInf[i].toString();
                var dom_des = $($.parseHTML(strarr2dInf));
                destination = dom_des.find('.des').text();
                service.getDistanceMatrix({
                    origins: [source],
                    destinations: [destination],
                    travelMode: google.maps.TravelMode.DRIVING,
                    unitSystem: google.maps.UnitSystem.METRIC,
                    avoidHighways: false,
                    avoidTolls: false
                }, function (response, status) {
                    if (status == google.maps.DistanceMatrixStatus.OK && response.rows[0].elements[0].status != "ZERO_RESULTS") {
                        distance = response.rows[0].elements[0].distance.text;
                        var spDistance = $("#distance" + j);
                        spDistance.prepend(distance);

                        var li = $("#" + j);
                        var distancewithoutkmtext = distance.replace(' km', '');
                        li.attr("id", distancewithoutkmtext);

                        j++;
                    } else {
                            alert("Unable to find the distance via road.");
                        }
                    });
            }



            init_map('map_canvas', 18, arr2dLoc, arr2dInf);
        }
        else {
            $("#divMapContainer").hide();
            $("#placeholderServiceagent").hide();
            $("#noserviceagentstatus").show();
        }
        $("#lblServiceAgentStatus").html(result.Message);

    },
    complete: function (data) {

        var elems = $('.storeList').children('li');
        elems.each(function (idx, li) {
            alert($(this).attr("id"));
        });
    }

Ya he cambiado la identificación de la etiqueta li así:

 var li = $("#" + j);
 var distancewithoutkmtext = distance.replace(' km', '');
 li.attr("id", distancewithoutkmtext);

Y el html de salida se representa así: ingrese la descripción de la imagen aquí cuál es correcto (con los nuevos identificadores se actualiza)

Pero en función completa, cuando trato de probar llamando a la función de alerta para mostrar los identificadores de las etiquetas <li>, el antiguo valor de id se muestra así: ingrese la descripción de la imagen aquí lo cual es incorrecto (1,2,3,4 ... son valores originales de id de etiquetas li)

¿Pueden ayudarme chicos? Cualquier ayuda será muy apreciada. Muchas gracias.

0
Huy Ta 23 nov. 2017 a las 10:57

2 respuestas

La mejor respuesta

Me etiquetaron mucho en su código ahora, así que intentaré explicar las cosas en un ejemplo

Digamos que podemos reescribir su código para que se vea así:

$.ajax({
  type: "post",
  url: "/FindAVet/Search",
  // etc
  success: function successHandler(result) {
    // ...
  },
  complete: function completeHandler() {
    // ...
  }
})

Ahora, echemos un vistazo al interior del SuccessHadler

function successHandler(result) {
  // Do stuff with html
  // parse result
  // for each location;info pair:
  //   call service.getDistanceMatrix
  //   parse distanceMatrix
  //   modify DOM accordingly
}

En completeHandler, presumiblemente, desea seguir trabajando con el DOM modificado, pero no puede. Y eso es porque la llamada a service.getDistanceMatrix es asincrónica. En realidad, $.ajax

  1. emite una solicitud
  2. murga. <--- esto es muy importante. No es una simple espera. usted esencialmente cede el control de ejecución, permitiendo que otro código se ejecute. El bucle de eventos se hace cargo aquí, para más detalles, consulte https://developer.mozilla.org/cs/docs/Web/JavaScript/EventLoop
  3. después de que llega la respuesta, llamadas exitosas (mientras esperamos, esto se coloca en la cola del bucle de eventos)
  4. una vez finalizado el éxito, las llamadas se completan (de nuevo, en cola inmediatamente después del éxito)

Eso de esperar que sucede, eso sucede con cada llamada asincrónica. Y como service.getDistanceMatrix es asíncrono, después de llamar a Google, ESPERA. entonces, ajax hace 1, 2, 3. Durante 3. se llama a service.getDistanceMatrix.

El interior de service.getDistanceMatrix tiene un mecanismo similar al de $ .ajax. De nuevo, espera. Ahora, si algo espera, el bucle de eventos busca en la cola eventos en los que podría funcionar mientras tanto. Y ahora ve que hay un paso 4. del anterior $.ajax, así que felizmente va a trabajar con él. Después de unos ms, service.getDistanceMatrix también obtiene datos y dispara su devolución de llamada, que contiene:

//   parse distanceMatrix
//   modify DOM accordingly

Pero eso es demasiado tarde, ya que completeHandler ya terminó

Entonces, ¿cómo salir de esto?

La solución más sencilla sería poner el código del completeHandler dentro del successHandler. Pero eso no es todo.

$.ajax({
  type: "post",
  url: "/FindAVet/Search",
  // etc
  success: function successHandler(result) {
    // Do stuff with html
    // parse result
    // for each location;info pair:
    //   call service.getDistanceMatrix
    //   parse distanceMatrix
    //   modify DOM accordingly

    completeHandler();
  }
})

El controlador completo ahora todavía no espera a que finalicen las llamadas asincrónicas a getDistanceMatrix. Podemos envolver el service.getDistanceMatrix en una promesa para que sea asincrónico:

const service = new google.maps.DistanceMatrixService()
function getDistanceMatrixAsync(parameters) {
  return new Promise(function(resolve, reject) {
    service.getDistanceMatrix(parameters, function(response, status) {
      if (status == google.maps.DistanceMatrixStatus.OK && response.rows[0].elements[0].status != "ZERO_RESULTS") {
        resolve(response)
      }
      else {
        reject('Unable to find the distance via road.')
      }
    })
  })
}

// usage:

// outside the loop - this is needed because of the asynchronous code inside the for loop.
let k = 1

// inside the loop
getDistanceMatrixAsync({
  origins: [source],
  destinations: [destination],
  travelMode: google.maps.TravelMode.DRIVING,
  unitSystem: google.maps.UnitSystem.METRIC,
  avoidHighways: false,
  avoidTolls: false
})
.then(function (response) {
  let j = k;
  distance = response.rows[0].elements[0].distance.text;
  var spDistance = $("#distance" + j);
  spDistance.prepend(distance);

  var li = $("#" + j);
  var distancewithoutkmtext = distance.replace(' km', '');
  li.attr("id", distancewithoutkmtext)
})
.catch(function (error) {
  alert(error)
})

k++;

Aún así, no hemos terminado. El resultado de esta llamada sigue siendo Promise. Siendo una llamada asincrónica, completeHandler no espera en esto. Pero ya casi llegamos. Necesitamos producir un mecanismo de sincronización llamado Barrera. No es nada lujoso, solo espera a que finalicen varias tareas asincrónicas (promesas), y una vez all están listos, ejecuta el código que desee. ¡Pero para eso, necesitamos crear una variedad de estas Promesas!

  // create array for promises just before the for loop
  var promises = []

  // add the promise to the array
  var promise = getDistanceMatrixAsync(/* data... */).then(/* stuff with the distance */).catch(/*optional error handling*/)
  promises.push(promise)

  // finally, outside and after the for loop, create a barrier!
  Promise.all(promises).then(function() { completeHandler() })

¡Así que este es el camino más corto hacia el éxito! Está increíblemente lejos de ser limpio y fácil de mantener, pero debería estar funcionando. Si está interesado en mejorar la capacidad de mantenimiento, envíeme un ping y también podremos hacerlo.

Lectura recomendada:


Editar:

Error solucionado con ID en then getDistanceMatrixAsync. Tenga en cuenta que cambia la lógica de numeración de ID para las etiquetas li debido al hecho de que las resoluciones de Promise pueden llegar desordenadas. Sugeriría usar ID sustitutos, es decir, ID que no está vinculado a ninguna lógica dentro de su aplicación (actualmente usa la posición en una lista, lo que conduce al acoplamiento)

0
netchkin 24 nov. 2017 a las 07:03

Está cambiando los ID en la función de devolución de llamada asincrónica de service.getDistanceMatrix(). Pero estás alertando a los ID en la función de devolución de llamada del $.ajax original, por lo que la segunda función de devolución de llamada aún no se ha ejecutado.

Mueva el bucle con alert a esa segunda función de devolución de llamada y verá los resultados correctos.

$.ajax({
  type: "post",
  async: false,
  url: "/FindAVet/Search",
  data: '{"vetname":"' + $("#VetName").val() + '","lat":"' + objlatitude + '","lng":"' + objlongitude + '","radius":"' + $("#hdnRadius").val() + '","searchAll":"' + searchAll + '"}',
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  success: function(result) {
    if (result.IsValid == true) {
      $("#divMapContainer").show();
      $("#placeholderServiceagent").html('');
      $("#placeholderServiceagent").show();
      $("#placeholderServiceagent").html(result.Datavalue);
      $("#noserviceagentstatus").hide();

      var arrLoc = result.Locations.split(";");
      var arrInf = result.InfoWindowContents.split(";");

      var source, destination, distance;
      source = $("#SuburbOrPostcode").val();

      var service = new google.maps.DistanceMatrixService();

      var arr2dLoc = [];
      var arr2dInf = [];
      for (var i = 0; i < arrLoc.length; i++) {
        arr2dLoc[i] = arrLoc[i].split(",");
      }
      var j = 1;
      for (var i = 0; i < arrInf.length; i++) {

        arr2dInf[i] = arrInf[i].split(",");
        var strarr2dInf = arr2dInf[i].toString();
        var dom_des = $($.parseHTML(strarr2dInf));
        destination = dom_des.find('.des').text();
        service.getDistanceMatrix({
          origins: [source],
          destinations: [destination],
          travelMode: google.maps.TravelMode.DRIVING,
          unitSystem: google.maps.UnitSystem.METRIC,
          avoidHighways: false,
          avoidTolls: false
        }, function(response, status) {
          if (status == google.maps.DistanceMatrixStatus.OK && response.rows[0].elements[0].status != "ZERO_RESULTS") {
            distance = response.rows[0].elements[0].distance.text;
            var spDistance = $("#distance" + j);
            spDistance.prepend(distance);

            var li = $("#" + j);
            var distancewithoutkmtext = distance.replace(' km', '');
            li.attr("id", distancewithoutkmtext);

            j++;
          } else {
            alert("Unable to find the distance via road.");
          }
          var elems = $('.storeList').children('li');
          elems.each(function(idx, li) {
            alert($(this).attr("id"));
          });
        });
      }
      init_map('map_canvas', 18, arr2dLoc, arr2dInf);
    } else {
      $("#divMapContainer").hide();
      $("#placeholderServiceagent").hide();
      $("#noserviceagentstatus").show();
    }
    $("#lblServiceAgentStatus").html(result.Message);
  }
});
0
Barmar 23 nov. 2017 a las 10:47