1

I am running a loop & within the loop I am calling a function. The function makes an ajax call & returns response but the problem is loop is not waiting for the response therefore the function returns undefined.

function checkBidStatus(index) {
  $.ajax({
     type:'get',
     url:'/orderbook/checkorderdetails',
     success: function(data){
        $parsedData = JSON.parse(data);
        if($parsedData[index]) {
          return "Hello";
        } else {
          return "Bye";
        }

        //$("#bid-details .modal-dialog").html(data);
     },
    error: function (response) {
      console.log(response);
    }
  });
}

let content = '';
 for(let i = 1; i <= 10; i++ ) {
    content += '<div>' + 
    (checkBidStatus(i)) +
    '</div>';
 }
Roy Bogado
  • 4,299
  • 1
  • 15
  • 31
A J
  • 441
  • 4
  • 20
  • 1
    The problem is that the for loop is synchronous, but the ajax call is asynchronous. I.e., that function has to wait for the server to respond. Therefore, callbacks or promises need to be used by the caller before iterating to the next request. – RichS Apr 02 '19 at 05:43
  • plz elaborate with code – A J Apr 02 '19 at 05:46
  • Ok... in progress. Code coming soon. – RichS Apr 02 '19 at 05:48
  • You can make the ajax call synchronous, by defining the `async:false` in ajax body. this is the easy solution but it also block your ui thread, that means your ui will non responding during the loop. – Ayan_84 Apr 02 '19 at 05:55
  • 1
    @ayan_84 synchronous Ajax is deprecated due to the poor user experience it provides. It locks the browser during use and a long running request (or series of requests) can give the user the impression that the page has crashed. Also since it's deprecated you can expect browsers to stop supporting it in the foreseeable future. This is not a good way to solve the problem. Promise chaining would be a more suitable solution – ADyson Apr 02 '19 at 05:58
  • @Adyson, that is clearly not recommended, but currently it'll solve his purpose – Ayan_84 Apr 02 '19 at 05:59
  • @AJ, Also your code need some correction, returning from success callback actually dont work as returning from checkBidStatus(). Try to use a local variable to checkBidStatus() and then assign this variable in the success callback – Ayan_84 Apr 02 '19 at 06:03
  • Possible duplicate of [Javascript - AJAX request inside loops](https://stackoverflow.com/questions/22621689/javascript-ajax-request-inside-loops) – VLAZ Apr 02 '19 at 06:06
  • I just provided a possible implementation using promises (see also: [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises)). There are much more elegant/ideal solutions out there that take advantage of libraries, but the code I provide is meant to explain the issue. – RichS Apr 02 '19 at 06:17

3 Answers3

2

The problem is that the for loop is synchronous, but the ajax call is asynchronous. I.e., that function has to wait for the server to respond. Therefore, callbacks or promises need to be used by the caller before iterating to the next request.

There are elegant ways to accomplish this task using certain libraries, such as async, but the "quick vanillaJS" way to solve this task using promises would be something like this:

const contentTemplate = '<div>[BID_RESULT]</div>';
const nRequests = 10;
var requestCnt = 0;
var content = '';

function checkBidStatus(index) {
    return new Promise((resolve, reject) => {
        $.ajax({
            type:'get',
            url:'/orderbook/checkorderdetails',
            success: function(data){
               $parsedData = JSON.parse(data);
               resolve({ index: index + 1, msg: $parsedData ? "Hello" : "Bye" });
            },
            error: reject
        });
        // <-- NOTE: there is no return statement at this line. Hence if 
        // you were to call this function from your for loop, you'd see 
        // undefined
    }
}

Then, you can keep calling that function recursively:

function multipleRequests(){
    checkBidStatus(requestCnt)
    .then((resp) => { // This runs if the promise RESOLVES (i.e., success)
        content = contentTemplate.replace('[BID_RESULT]', resp.msg);
        // --- [then do what you want with content] --- //
        // Request again
        requestCnt++;
        if( requestCnt < nRequests ){
            multipleRequests();   
        }
    })
    .catch(console.error) // This runs if promise rejects (i.e., has an error)
}

// Run:
multipleRequests();
RichS
  • 913
  • 4
  • 12
1

Why not use a Promise inside and wait on that promise to get the required result, after which you update the DOM? In your checkBidStatus method, you can create a promise and return that. When the ajax result is available, resolve the promise with ajax result.

The caller of the method would need to wait for the promise to resolve and then update the DOM.

let content = '';
for(let i = 1; i <= 10; i++ ) {
  checkBidStatus(i).then(result => {
    content += '<div>' +result + '</div>';
  })
}

Note: I have not tested the code, but something similar should work. Also the order of divs created base on the index is not guaranteed, so you may need to take care of that, if order is important.

Anand Nath
  • 31
  • 4
  • 1
    I think this pretty nice. I think a combination of our answers should get him going :) – RichS Apr 02 '19 at 06:31
0

You should wait till response from server for every iteration in loop.

function checkBidStatus(index) {
$.ajax({
    type: 'get',
    url: '/orderbook/checkorderdetails',
    success: function(data) {
        $parsedData = JSON.parse(data);
        if ($parsedData[index]) {
            return {
                msg: 'Hello',
                index: index + 1,
            };
        } else {
            return {
                msg: 'Bye',
                index: index + 1,
            };
        }

        //$("#bid-details .modal-dialog").html(data);
    },
    error: function(response) {
        console.log(response);
    },
});

}

let content = '';
let i = 1;
while (i <= 10) {
    let { msg, index } = checkBidStatus(i);
    content += '<div>' + msg + '</div>';
    i = index;
}