Karl
Karl

Reputation: 53

Javascript/Jquery How to wait for AJAX function to complete when called from a timer?

I have a function that I call from a from a timeout that makes an ajax call. I also have a button click call that I don't want to execute until the ajax call completes, if the ajax call is running. If the ajax call is not running, I want to run the button click code.

What is the best way to handle this? With a variable that you check to see if the ajax function is running or is there another way?

Here is my pseudo code

setTimeout(doAjax, 5000);

function doAjax() {
  $.ajax({
        url: "xxx.com/dosomething",
        dataType: 'json',
        type: 'POST',
        cache: false,
        contentType: 'application/json; charset=utf-8',
        success: function (data) { doSuccess() },
        error: function (data, status, jqXHR) { doError() }
    });
}

function myBtnClick() {
  // wait for doAjax call to complete if running
}

Upvotes: 2

Views: 448

Answers (2)

Lajos Arpad
Lajos Arpad

Reputation: 76454

You can achieve what you want by making sure that your prerequisite async operations you have are promises and, if you have an array where you store them, then you can do something like this:

Promise.all(yourArray).then((values) => {
    /*Do something*/
})

Proof-of-concept:

function decreaseCounter() {
    let context = document.getElementById("counter");
    let value = parseInt(context.innerText);
    context.innerText = value - 1;
}
function test() {
    Promise.all(myPromises).then(() => {
        alert("Job Complete!");
    });
}
let myPromises = [
    new Promise(resolve => setTimeout(resolve, 1000)).then(decreaseCounter),
    new Promise(resolve => setTimeout(resolve, 2000)).then(decreaseCounter),
    new Promise(resolve => setTimeout(resolve, 3000)).then(decreaseCounter),
    new Promise(resolve => setTimeout(resolve, 4000)).then(decreaseCounter),
    new Promise(resolve => setTimeout(resolve, 5000)).then(decreaseCounter),
    new Promise(resolve => setTimeout(resolve, 6000)).then(decreaseCounter),
    new Promise(resolve => setTimeout(resolve, 7000)).then(decreaseCounter),
    new Promise(resolve => setTimeout(resolve, 8000)).then(decreaseCounter),
    new Promise(resolve => setTimeout(resolve, 9000)).then(decreaseCounter),
    new Promise(resolve => setTimeout(resolve, 10000)).then(decreaseCounter)
]
This is the final countdown:
<h1 id="counter">10</h1>

<b>Click on the button to see whether it waits for the countdown</b><br>
<input type="button" onclick="test()" value="Click Me">

Upvotes: 1

Phil
Phil

Reputation: 164767

The solution here is to use promises to maintain the state of your async code.

let clickDelayPromise = Promise.resolve(); // initial value

async function myBtnClick() {
  // wait for the promise to resolve
  await clickDelayPromise;

  // now do stuff
}

function doAjax() {
  // assign the $.ajax deferred (promise) to the variable
  clickDelayPromise = $.ajax({
    // ...
  });
}

Initially, the clickDelayPromise will resolve immediately but once the AJAX call begins, the promise will only resolve once the request completes.

// mocks
const $ = {ajax:()=>(console.log("ajax starting"),new Promise(r=>setTimeout(()=>{console.log("ajax done");r();},4000)))};

let clickDelayPromise = Promise.resolve(); // initial value

async function myBtnClick() {
  // wait for the promise to resolve
  await clickDelayPromise;

  // now do stuff
  console.log("button click!");
}

function doAjax() {
  // assign the $.ajax deferred (promise) to the variable
  clickDelayPromise = $.ajax({
    // ...
  });
}

setTimeout(doAjax, 5000);
<button onclick="myBtnClick()">Click me lots</button>

Upvotes: 1

Related Questions