Thanasis Petsas
Thanasis Petsas

Reputation: 4448

How to force a program to wait until an HTTP request is finished in JavaScript?

Is there a way in JavaScript to send an HTTP request to an HTTP server and wait until the server responds with a reply? I want my program to wait until the server replies and not to execute any other command that is after this request. If the HTTP server is down I want the HTTP request to be repeated after a timeout until the server replies, and then the execution of the program can continue normally.

Any ideas?

Thank you in advance, Thanasis

Upvotes: 34

Views: 156346

Answers (8)

Jérôme Beau
Jérôme Beau

Reputation: 11450

EDIT: Synchronous requests are now deprecated; you should always handle HTTP requests in an async way.

There is a 3rd parameter to XmlHttpRequest's open(), which aims to indicate that you want the request to by asynchronous (and so handle the response through an onreadystatechange handler).

So if you want it to be synchronous (i.e. wait for the answer), just specify false for this 3rd argument. You may also want to set a limited timeout property for your request in this case, as it would block the page until reception.

Here is an all-in-one sample function for both sync and async:

function httpRequest(address, reqType, asyncProc) {
  var req = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
  if (asyncProc) { 
    req.onreadystatechange = function() { 
      if (this.readyState == 4) {
        asyncProc(this);
      } 
    };
  }
  req.open(reqType, address, !(!asyncProc));
  req.send();
  return req;
}

which you could call this way:

var req = httpRequest("http://example.com/aPageToTestForExistence.html", "HEAD");  // In this example you don't want to GET the full page contents
alert(req.status == 200 ? "found!" : "failed");  // We didn't provided an async proc so this will be executed after request completion only

Upvotes: 51

Carson
Carson

Reputation: 7968

For the modern browser, I will use the fetch instead of XMLHttpRequest.

async function job() {
  const response = await fetch("https://api.ipify.org?format=json", {}) // type: Promise<Response>
  if (!response.ok) {
    throw Error(response.statusText)
  }
  return response.text()
}


async function onCommit() {
  const result = await job()
  // The following will run after the `job` is finished.
  console.log(result)
}

an examples

<button onclick="onCommit()">Commit</button>
<script>
  function onCommit() {
    new Promise((resolve, reject) => {
      resolve(job1())
    }).then(job1Result => {
      return job2(job1Result)
    }).then(job2Result => {
      return job3(job2Result)
    }).catch(err => { // If job1, job2, job3, any of them throw the error, then will catch it.
      alert(err)
    })
  }

  async function testFunc(url, options) {
    // options: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
    const response = await fetch(url, options) // type: Promise<Response>
    if (!response.ok) {
      const errMsg = await response.text()
      throw Error(`${response.statusText} (${response.status}) | ${errMsg} `)
    }
    return response
  }

  async function job1() {
    console.log("job1")
    const response =  await testFunc("https://api.ipify.org?format=json", {})
    return await response.json()
  }

  async function job2(job1Data) {
    console.log("job2")
    console.log(job1Data)
    const textHeaders = new Headers()
    textHeaders.append('Content-Type', 'text/plain; charset-utf-8')
    const options = {"headers": textHeaders}
    const response = await testFunc("https://api.ipify.org/?format=text", options)
    // throw Error(`test error`) // You can cancel the comment to trigger the error.
    return await response.text()
  }

  function job3(job2Data) {
    console.log("job3")
    console.log(job2Data)
  }
</script>

Upvotes: 2

Skyler Jokiel
Skyler Jokiel

Reputation: 318

This is an old question but wanted to provide a different take.

This is an async function that creates a promise that resolves with the Http object when the request is complete. This allow you to use more modern async/await syntax when working with XMLHttpRequest.

async sendRequest() {
    const Http = new XMLHttpRequest();
    const url='http://localhost:8000/';
    Http.open("GET", url);
    Http.send();

    if (Http.readyState === XMLHttpRequest.DONE) {
        return Http;
    }

    let res;
    const p = new Promise((r) => res = r);
    Http.onreadystatechange = () => {
        if (Http.readyState === XMLHttpRequest.DONE) {
            res(Http);
        }
    }
    return p;
}

Usage

const response = await sendRequest();
const status = response.status;
if (status === 0 || (status >= 200 && status < 400)) {
    // The request has been completed successfully
    console.log(response.responseText);
} else {
    // Oh no! There has been an error with the request!
    console.log(`Server Error: ${response.status}`)
}

Upvotes: 0

applemonkey496
applemonkey496

Reputation: 743

For those using axios, you can wrap it in an async iife and then await it:

(async () => {
  let res = await axios.get('https://example.com');

  // do stuff with the response
})();

Note, I haven't done any error checking here.

Upvotes: -1

Ashif Nataliya
Ashif Nataliya

Reputation: 922

You can use XMLHttpRequest object to send your request. Once request is sent, you can check readyState property to identify current state. readyState will have following different states.

  • Uninitialized - Has not started loading yet
  • Loading - Is loading
  • Interactive - Has loaded enough and the user can interact with it
  • Complete - Fully loaded

for example:

xmlhttp.open("GET","somepage.xml",true);
xmlhttp.onreadystatechange = checkData;
xmlhttp.send(null);

function checkData()
{
    alert(xmlhttp.readyState);
}

hope this will help

Upvotes: 8

jwueller
jwueller

Reputation: 30996

You can perform a synchronous request. jQuery example:

$(function() {
    $.ajax({
       async: false,
       // other parameters
    });
});

You should take a look at jQuery's AJAX API. I highly recommend using a framework like jQuery for this stuff. Manually doing cross-browser ajax is a real pain!

Upvotes: 8

Argenti Apparatus
Argenti Apparatus

Reputation: 4013

I have a similar situation in an game built with Three.js and Google Closure. I have to load 2 resources, Three and Closure do not allow me to make these synchronous.

Initially I naively wrote the following:

main() {

  ...
  var loaded=0;
  ...

  // Load Three geometry
  var loader = new THREE.JSONLoader();
  loader.load("x/data.three.json", function(geometry) {
    ...
    loaded++;
    });

   // Load my engine data
  goog.net.XhrIo.send("x/data.engine.json", function(e) {
    var obj = e.target.getResponseJson();
    ...
    loaded++;
    });

  // Wait for callbacks to complete
  while(loaded<2) {}

  // Initiate an animation loop
  ...
};

The loop that waits for the callbacks to complete never ends, from the point of view of the loop loaded never get incremented. The problem is that the callbacks are not fired until main returns (at least on Chrome anyway).

One solution might be to have both callbacks check to see if it's the last to complete, and them go on to initiate the animation loop.

Another solution - perhaps a more direct answer to what you are asking (how to wait for each load before initiating another) - would be to nest the callbacks as follows:

// Load Three geometry
var loader = new THREE.JSONLoader();
loader.load("x/data.three.json", function(geometry) {
  ...

   // Load my engine data
   goog.net.XhrIo.send("x/data.engine.json", function(e) {
     var obj = e.target.getResponseJson();
     ...

     // Initiate an animation loop
     ...

    });
  });
};

Upvotes: 0

Nik
Nik

Reputation: 4075

For this you can start loader in javascript as soon as page starts loading and then you can close it when request finishes or your dom is ready. What i am trying to say, as page load starts, start a loader . Then page can do multiple synchronous request using ajax , until and unless you didn't get response, do not close close loader. After receiving the desired in response in final call, you can close the loader.

Upvotes: 0

Related Questions