繁体   English   中英

如何强制程序在 JavaScript 中等待 HTTP 请求完成?

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

JavaScript 中有没有办法将 HTTP 请求发送到 HTTP 服务器并等待服务器响应回复? 我希望我的程序等到服务器回复并且不执行此请求之后的任何其他命令。 如果HTTP服务器宕机我希望超时后重复HTTP请求,直到服务器回复,然后程序的执行才能正常继续。

有什么想法吗?

提前谢谢你,塔纳西斯

XmlHttpRequestopen()有第三个参数,它旨在表明您希望通过异步请求(因此通过onreadystatechange处理程序处理响应)。

因此,如果您希望它是同步的(即等待答案),只需为此第三个参数指定 false。 在这种情况下,您可能还想为您的请求设置一个有限的timeout属性,因为它会阻止页面直到接收为止。

这是同步和异步的多合一示例函数:

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);
      } 
    };
  } else { 
    req.timeout = 4000;  // Reduce default 2mn-like timeout to 4 s if synchronous
  }
  req.open(reqType, address, !(!asyncProc));
  req.send();
  return req;
}

你可以这样称呼它:

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

您可以执行同步请求。 jQuery 示例:

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

你应该看看 jQuery's AJAX API 我强烈建议使用像 jQuery 这样的框架来处理这些东西。 手动做跨浏览器ajax真的很痛苦!

您可以使用 XMLHttpRequest 对象来发送您的请求。 发送请求后,您可以检查 readyState 属性以识别当前状态。 readyState 将有以下不同的状态。

  • 未初始化 - 尚未开始加载
  • 正在加载 - 正在加载
  • 交互式 - 已加载足够并且用户可以与之交互
  • 完成 - 满载

例如:

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

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

希望这会有所帮助

为此,您可以在页面开始加载后立即在 javascript 中启动加载器,然后在请求完成或 dom 准备就绪时关闭它。 我想说的是,当页面加载开始时,启动一个 loader 。 然后页面可以使用 ajax 执行多个同步请求,直到并且除非您没有得到响应,否则不要关闭关闭加载器。 在最终调用中收到所需的响应后,您可以关闭加载程序。

这是一个老问题,但想提供不同的看法。

这是一个异步函数,它创建一个在请求完成时使用 Http 对象解析的承诺。 这允许您在处理 XMLHttpRequest 时使用更现代的 async/await 语法。

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;
}

用法

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}`)
}

对于现代浏览器,我将使用fetch而不是 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)
}

一个例子

 <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>

我在用 Three.js 和 Google Closure 构建的游戏中遇到了类似的情况。 我必须加载 2 个资源,三个和 Closure 不允许我使这些同步。

最初我天真地写了以下内容:

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
  ...
};

等待回调完成的循环永远不会结束,从loaded的循环的角度来看,永远不会增加。 问题是在main返回之前不会触发回调(至少在 Chrome 上)。

一种解决方案可能是让两个回调都检查它是否是最后一个完成的,然后它们继续启动动画循环。

另一个解决方案——也许是对你所问的更直接的回答(如何在启动另一个之前等待每个负载)——将回调嵌套如下:

// 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
     ...

    });
  });
};

对于那些使用axios 的人,您可以将其包装在async iife 中,然后await它:

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

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

注意,我这里没有做任何错误检查。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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