【问题标题】:Race between fetch requests获取请求之间的竞争
【发布时间】:2020-10-16 00:53:17
【问题描述】:

我正在开发一个跟踪器,它应该收集我们客户网站上的一些数据,并在网站用户离开页面时使用 fetch 请求将其发送到我们的 api。

这个想法是使用beforeunload 事件处理程序来发送请求,但我读过here,为了覆盖大多数浏览器,我还需要使用unload 事件处理程序。

这是我们的客户将在其网站上放置的跟踪代码的相关部分:

var requestSent = false;
function submitData(element_id, url) {
    
    if (!requestSent) {
        
        var data = JSON.stringify({ourobject});
    
    
        fetch(url, {
           method: 'POST',
           headers: {
             'Accept': 'application/json',
             'Content-Type':'application/x-www-form-urlencoded',
           },
          body: data,})
        .then(response => response.json())
        .then((data) => {
           console.log('Hello?');
           requestSent = true;
        });
    } 
}

window.addEventListener('beforeunload', function (e) { submitData(1, "https://oursiteurl/metrics");});
window.addEventListener('unload', function(event) {submitData(1, "https://oursiteurl/metrics"); });

我在 chrome 上对此进行了测试,两个请求都通过了,而不仅仅是第一个成功,这会导致我们的数据库中出现重复数据。

在将控制台登录放在 requestSent 标志设置为 true 的部分旁边后,我意识到部分代码永远不会被执行。

如果我在网络选项卡中保留日志,则表示两个请求都已取消,即使数据到达我们的端点

我们的 api 是在 Codeigniter 中创建的,这里是 /metrics 端点

public function submit () {
      
      $this->cors();

      $response = [
          'status' => 'error',
          'message' => 'No data',
      ];
      
      $data = json_decode(file_get_contents('php://input'), true);
      if (empty($data)) {
          echo json_encode($response);exit();
      }
      // process data and do other stuff ...

Cors 函数:

private function cors() {

      // Allow from any origin
      if (isset($_SERVER['HTTP_ORIGIN'])) {
          // Decide if the origin in $_SERVER['HTTP_ORIGIN'] is one
          // you want to allow, and if so:
          header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
          header('Access-Control-Allow-Credentials: true');
          header('Access-Control-Max-Age: 86400');    // cache for 1 day
      }

      // Access-Control headers are received during OPTIONS requests
      if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {

          if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
              // may also be using PUT, PATCH, HEAD etc
              header("Access-Control-Allow-Methods: GET, POST, OPTIONS");         

          if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
              header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");

      }

  }

编辑:

感谢 @CBroe 建议使用 Beacon API,使用它消除了对 unload 和 beforeunload 事件处理程序的需要:

submitData 现在看起来像这样:

...
if (navigator.sendBeacon) {
        let beacon = navigator.sendBeacon(url, data);
        console.log( 'Beacon', beacon );
    } else { // fallback for older browsers
        if (!requestSent) {
            console.log( 'Data object from fallback', data );
            var xhr = new XMLHttpRequest();
            xhr.open("POST", url, false); // third parameter of `false` means synchronous
            xhr.send(data);
    }
 ... 

这样做让我只保留 beforeunload 事件处理程序,因为它适用于 ie 和 chrome:

 window.addEventListener('beforeunload', function (e) { submitData(1, "https://oursiteurl/metrics");});

【问题讨论】:

  • 你应该使用的是developer.mozilla.org/en-US/docs/Web/API/Beacon_API,而不是 AJAX 或 fetch (如果你可以在缺乏 IE 支持的情况下做。如果你不能,那么我仍然会在浏览器上使用它支持它,并且可能实现 AJAX/fetch 作为 IE 的后备。)
  • @CBroe 感谢您的建议,我将为支持它的浏览器切换到信标 api,但我需要它在尽可能多的浏览器上工作,所以我仍然需要使用它作为备份。
  • @CBroe 您的建议基本上为我解决了问题,如果您对我的解决方案感兴趣,我已经编辑了我的问题,如果您将其发布为答案,我会接受。谢谢!

标签: javascript php ajax api fetch


【解决方案1】:

想法是使用beforeunload 事件处理程序来发送请求,但我在这里读到为了覆盖大多数浏览器,我还需要使用unload 事件处理程序。

两者都不太适合发出 AJAX/fetch 请求,它们很可能在页面实际卸载时被取消。

你应该使用Beacon API,它是专门为这种跟踪/保持活动请求而设计的。

根据 MDN 上的浏览​​器兼容性列表,Internet Explorer 尚不支持它。如果您也需要跟踪,可能会采用双管齐下的方法 - 支持它的浏览器的 Beacon,IE 的 AJAX/fetch 回退。

【讨论】:

    猜你喜欢
    • 2018-09-03
    • 2020-08-14
    • 2022-11-18
    • 2016-11-16
    • 1970-01-01
    • 1970-01-01
    • 2016-02-02
    • 2013-11-14
    • 2015-05-16
    相关资源
    最近更新 更多