44

I am trying to write a JS code that will cancel the "btn_submit" buttons .onclick event if the given number already exists in the database. I use AJAX to query the DB for the given number and to determine if the should send the data to a .php site which will upload the question. To determine this I need the numOfRows variable's value, but because I set it in AJAX it will stay on 0. The validation() function will finish before my AJAX query finishes and this causes the problem that will always state that the given number does not exist in the DB (numOfRows will always stay on 0). How can I await the AJAX query's finish before I compare the numOfRows to 0 in my validation() function's ending lines? If the number already exists in the DB, I need to return false to this line:

document.getElementById("btn_submit").onclick = validation;

Thank you!

var textAreaList;
var numOfRows = 0;
var finished = false;

document.getElementById("btn_submit").onclick = validation;

textAreaList = document.getElementsByClassName("text_input");

function validation() {
    loadNumRows();

    try {
        document.getElementById('failure').hidden = true;
    }
     catch(e) {
         console.log(e.message);
     }
    textAreaList = document.getElementsByClassName("text_input");
    var failValidation = false;
    for (var i = 0; i < textAreaList.length; i++) {
        console.log(textAreaList[i]);
        if (textAreaList[i].value == "") {
            textAreaList[i].style.border = "2px solid #ff0000";
            failValidation = true;
        } else {
            textAreaList[i].style.border = "2px solid #286C2B";
        }
    }

    return !(failValidation || numOfRows != 0);
}

function loadNumRows(){
    $.ajax({
        url: 'php/SeeIfNumberExists?number=' + document.getElementById('number_inp').value,
        type: "GET",
        cache: false,
        success: function (html) {
           numOfRows = parseInt(html);               
        }
    });
}
masm64
  • 1,222
  • 3
  • 14
  • 31
  • one way is to bind the ajax request to the change event of form control that needs server side validating for num_rows. Don't allow form to submit or start other validation if that hasn't been returned or it failed – charlietfl Dec 23 '14 at 00:18
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Henke May 10 '21 at 11:36

5 Answers5

46

use of async/await with a transpilers like Babel to get it working in older browsers. You’ll also have to install this Babel preset and polyfill from npm:

npm i -D babel-preset-env babel-polyfill

Then

function getData(ajaxurl) { 
  return $.ajax({
    url: ajaxurl,
    type: 'GET',
  });
};

async function test() {
  try {
    const res = await getData('https://api.icndb.com/jokes/random')
    console.log(res)
  } catch(err) {
    console.log(err);
  }
}

test();

or the .then callback is just another way to write the same logic.

getData(ajaxurl).then((res) => {
    console.log(res)
});
Murtaza Hussain
  • 3,851
  • 24
  • 30
18

Using async: false is an extremely bad idea, and defeats the whole purpose of using AJAX at the first place — AJAX is meant to be asynchronous. If you want to wait for a response from your script when you make the AJAX call, simply use deferred objects and promises:

var validation = function () {
    var numberCheck = $.ajax({
        url: 'php/SeeIfNumberExists?number=' + $('#number_inp').val(),
        type: "GET"
    });

    // Listen to AJAX completion
    numberCheck.done(function(html) {
        var numOfRows = parseInt(html),
            textAreaList = $('.text_input'),
            finished = false;

        // Rest of your code starts here
        try {
            document.getElementById('failure').hidden = true;
        }
        catch(e) {
            console.log(e.message);
        }

        // ... and the rest
    });

}

// Bind events using jQuery
$('#btn_submit').click(validation);

I see in your code that you are using a mixture of both native JS and jQuery — it helps if you stick to one :)

Terry
  • 63,248
  • 15
  • 96
  • 118
  • Is there any advantage to storing the ajax request in a variable and use `.done` over just giving it a `success` and `error` methods? – developerwjk Dec 23 '14 at 00:33
  • 2
    @developerwjk The `jqXHR.success()` method has been officially deprecated as of v1.8. Use `jqXHR.done()` instead, as `$.ajax()` has [promises natively implemented](https://medium.com/coding-design/writing-better-ajax-8ee4a7fb95f), which will return a resolved deferred object when a response is received from the server. A resolved object will map to `.done()`, while a rejected one will map to `.fail()`. Both will map to `.always()`, though. – Terry Dec 23 '14 at 00:34
14

Never use async:false its dangerous, your app might misbehave.

You can use await only when your response returns a promise.

Unfortunately jQuery ajax doesn't return Promise when its completed.

But you can use promise in ajax request and return the promise when its done.

function asyncAjax(url){
    return new Promise(function(resolve, reject) {
            $.ajax({
                url: url,
                type: "GET",
                dataType: "json",
                beforeSend: function() {            
                },
                success: function(data) {
                    resolve(data) // Resolve promise and when success
                },
                error: function(err) {
                    reject(err) // Reject the promise and go to catch()
                }
            });
    });
}

We have converted ajax call into promise so now we can use await.

try{
    const result = await asyncAjax('your url');
} catch(e){
    console.log(e);
}
pranav shinde
  • 1,260
  • 13
  • 11
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! jQuery deferred are thenable already, you can directly `await` them. Or if you absolutely need a native promise, just [use `Promise.resolve(…)`](https://stackoverflow.com/a/31327725/1048572). – Bergi Jan 10 '22 at 19:36
  • _"Never use async:false its dangerous, your app might misbehave."_ Source? As far as I'm aware this isn't true. It's still not a good idea simply because it will block the UI, but so long as you're aware of that side-effect it's not inherently dangerous. – Clonkex Aug 15 '23 at 22:46
5

this works for me

async function doAjax() {
    const result = await $.ajax({
        url: "https://api.exchangerate-api.com/v4/latest/USD",
        type: 'GET',
    });

    return result;
}

async function tt(){
    var res = await doAjax()
    var money = res.rates.INR
    console.log(money)
}

tt()
Harshad pk
  • 71
  • 2
  • 4
2

(I acknowledge this is not the best way to go about things, but this is the quickest way to get your code working as is. Really though, you should rethink how you are pulling the numOfRows value so that it will work with truly asynchronous Ajax. All that being said...):

Start by setting async : false in the $.ajax call. The A in Ajax stands for asynchronous. That means, execution continues rather than waiting for it to return. You want to turn that off (i.e. make it synchronous). Actually, that should be the whole solution given the code you have there.

$.ajax({
        url: 'php/SeeIfNumberExists?number=' + document.getElementById('number_inp').value,
        type: "GET",
        async: false,
        cache: false,
        success: function (html) {
           numOfRows = parseInt(html);               
        }
    });

One caveat from the docs for $.ajax:

Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation. Note that synchronous requests may temporarily lock the browser, disabling any actions while the request is active. As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is deprecated; you must use the success/error/complete callback options instead of the corresponding methods of the jqXHR object such as jqXHR.done() or the deprecated jqXHR.success().

developerwjk
  • 8,619
  • 2
  • 17
  • 33