【问题标题】:How to force a program to wait until an HTTP request is finished in JavaScript?如何强制程序等到 JavaScript 中的 HTTP 请求完成?
【发布时间】:2010-09-21 12:38:53
【问题描述】:

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

有什么想法吗?

提前谢谢你, 塔纳西斯

【问题讨论】:

  • 只是一个旁注,在 jQuery 中你正在寻找 ajaxStop

标签: javascript xmlhttprequest httprequest wait


【解决方案1】:

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

【讨论】:

  • 这将我带到了异步库,这使得这非常简单。 github.com/caolan/async 不要被包装垃圾所迷惑。您只需包含一个小的 javascript 文件 - async.js!
  • 我今天第一次尝试同步请求,因为我和OP有类似的需求。惊讶地收到投诉(在 Chrome 上)说:“InvalidAccessError:无法在 'XMLHttpRequest' 上执行 'open':同步请求不得设置超时。”。考虑到同步请求的性质,这样的限制似乎很疯狂,但这就是结果。奇怪!
【解决方案2】:

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

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

你应该看看 jQuery 的AJAX API。我强烈建议使用 jQuery 之类的框架来处理这些内容。手动做跨浏览器 ajax 真的很痛苦!

【讨论】:

  • 这是最好的方法!然而,很多人认为 JS 框架是邪恶的,并且倾向于自己编写所有代码。
  • 例如,我尝试过使用 XmlHttpRequest 以多种方式执行此操作,但我还没有找到一种方法来停止执行,直到我得到响应..
  • 亚历克斯,就是我!我就是其中之一! ;)
  • 如果您不需要 JS 框架,只需使用普通的 XmlHttpRequest 回调和来自 async.js github.com/caolan/async的 async.waterfall()
【解决方案3】:

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

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

例如:

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

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

希望这会有所帮助

【讨论】:

  • 我希望程序的执行是串行的,我不想使用回调。如果可能,我希望在“xmlhttp.send(null);”处停止程序的执行调用,然后在请求完成后继续。
  • 您可以使用 readyState 属性。可能你可以把它放在while循环中,这样在执行完成之前它不会进入下一行。此外,如果您可以预测执行时间,您可以使用 setTimeOut() 函数。有关此功能的更多信息,您可以在此处阅读。 w3schools.com/jsref/met_win_settimeout.asp
  • 这对我有用,谢谢。以下是可能的就绪状态:0:UNSENT, 1:OPENED,2:HEADERS_RECEIVED,3:LOADING,4:DONE
  • 这似乎比等待或阻塞直到执行要好
【解决方案4】:

对于现代浏览器,我将使用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>

【讨论】:

    【解决方案5】:

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

    【讨论】:

    • 你离开了......他在问关于暂停多个请求。
    • @Thanasis Petsas :请检查已编辑的帖子。希望这可以帮助你。
    • 你能发一个简单的例子吗?
    【解决方案6】:

    我在使用 Three.js 和 Google Closure 构建的游戏中遇到了类似的情况。我必须加载 2 个资源,Three 和 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
         ...
    
        });
      });
    };
    

    【讨论】:

    • 您好 Argenti,欢迎来到 stackoverflow!请尽可能清楚地回答您的问题。使用花括号或使用 ` 字符包装您的代码。如果可能,请尝试在发布之前查看是否可以运行某些内容。这些问题今后也可能会被提及,请努力尝试!
    • 我发现了这个: 注意:从 Gecko 30.0(Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27)开始,由于对用户体验的负面影响,主线程上的同步请求已被弃用. Mozilla Dev.Network
    【解决方案7】:

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

    这是一个异步函数,它创建一个在请求完成时使用 Http 对象解析的 Promise。这允许您在使用 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}`)
    }
    

    【讨论】:

      【解决方案8】:

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

      (async () => {
        let res = await axios.get('https://example.com');
      
        // do stuff with the response
      })();
      

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

      【讨论】:

      • 我知道这个问题是很久以前发布的,但我想我会指出这一点,因为我发现它很有用。
      猜你喜欢
      • 1970-01-01
      • 2018-07-19
      • 2017-01-04
      • 2017-07-04
      • 2014-10-19
      • 1970-01-01
      • 1970-01-01
      • 2021-06-04
      • 1970-01-01
      相关资源
      最近更新 更多