简体   繁体   中英

Wait for $.ajax result inside .each function

I have function that search for every element with a specific class:

$("#stepSurveyCtnId .questionCtnClass").each(function () {}

Inside each step, I check if a question is of type customer:

var type = $(this).children().data("question-type");

var isCustomerQuestion = false;

switch (type) {
    case "Name":
    case "Email":
        isCustomerQuestion = true;
        break;
}

If it's customer type, I get the next id of the customer's table from the database:

  if(isCustomerQuestion) {
      if (customerId == -1) {
          $.ajax({
              method: "POST",
              url: urlCustomerCreate, 
              success: function (ajaxData) {
                  customerId = ajaxData.NumericValue;
              }
          });
      } 
  }

The issue is that in the second iteration of the .each() function, customerId is still = -1, when it should be 1305 for example.

It seems that the execution don't stop in the $.ajax call, or the iterations are executed at the same time and the second iteration don't receive the customerId from the first iteration.

I'm still not 100% clear on sure on how everything is structured for you, but here is one way of handling asynchronicity in JavaScript (adapted from @ShubHam's answer)

function handleQuestion(questionElements, index, customerId) {
    if (questionIndex >= questionElements.length) return;

    var type = $(this).children().data("question-type");

    var isCustomerQuestion = false;

    switch (type) {
    case "Name":
    case "Email":
        isCustomerQuestion = true;
        break;
    }

    if(isCustomerQuestion) {
      if (customerId == -1) {
          $.ajax({
              method: "POST",
              url: urlCustomerCreate, 
              success: function (ajaxData) {
                  handleQuestion(questionElements, questionIndex + 1, ajaxData.NumericValue);
              }
          });
      } else {
        // Have ID now
        handleQuestion(questionElements, questionIndex + 1, customerId);
      }
  }
}

// Go
handleQuestion($("#stepSurveyCtnId .questionCtnClass"), 0, -1);

This will only continue to the next iteration after the success callback has been triggered.

Put logic inside one function (say function 1) and ajax call inside other function.

Call ajax function from function 1. Inside success call function 1 with required params

Update (example added):

 var x=['a','b','c'] var elm=document.getElementById('demo') x.forEach(function(temp){ elm.innerHTML=elm.innerHTML+temp }) 
 <div id='demo'></div> 

This can be converted to new logic as

 var x=['a','b','c'] function sethtml(temp,length,maxlength){ //here ajax call can be placed var elm=document.getElementById('demo') elm.innerHTML=elm.innerHTML+temp //inside success function of ajax traverse(length+1,maxlength) } function traverse(length,maxlength){ if(length>=maxlength) { //all calls done next steps to perform }else{ sethtml(x[length],length,maxlength) } } traverse(0,x.length) 
 <div id='demo'></div> 

Advice to be considered from Jamie-Day in comments: Check your logic for scope of improvement. Accessing db results in for each kind of scenario generally can be avoided(ideally it should be avoided for better user experience)

Change your ajax code. add "async: false" so that each code next to ajax will wait for ajax result

  if(isCustomerQuestion) {
      if (customerId == -1) {
          $.ajax({
              method: "POST",
              async: false,
              url: urlCustomerCreate, 
              success: function (ajaxData) {
                  customerId = ajaxData.NumericValue;
              }
          });
      } 
  }

The first A in AJAX stands for Asynchronous which means that the ajax calls would get executed and would not wait for the call to finish. This way we can let users interact with other elements on the page and provide a good user experience.

If we make the AJAX calls asynchronous by setting the async option to false, then the browser would wait for the ajax call to complete and users would not be able to interact with the elements until the call has completed. With the increase in number of calls, this blockage time would increase.

I would suggest you find a better alternative than this.

First, you need to think asynchronously. Code that need to run after the ajax should be called from the success function. You also want to add error function to handle server errors.

Second, to improve speed and bandwidth I'd reduce number of AJAX calls to a single one, by joining all IDs together in a single AJAX request. It require server-side changes and you did not provide the server-side, so I'll leave server side to you.

// Prepare ajax call
var customerData = [];
var customerCreateData = [];
$("#stepSurveyCtnId .questionCtnClass").each(function () {
    var type = $(this).children().data("question-type");
    var isCustomerQuestion = false;
    switch (type) {
        case "Name":
        case "Email":
            isCustomerQuestion = true;
            break;
    }

    // Set customerId and customerCreateData

    if(isCustomerQuestion) {
        if (customerId == -1) {
            customerCreateData.push(customerCreateData);
        }
    }
}); // end each

if (customerCreateData.length) {
      $.ajax({
              method: "POST",
              url: urlCustomerCreate,
              data: customerCreateData,
              success: function (ajaxData) {
                  customerData = ajaxData.customerData;
                  doAmazingThingsWithCustomers(customerData);
              },
              error: function(jqXHR, textStatus, errorThrown) {
                  alert('Server error: ' + errorThrown);
              }
          });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM