是否可以在无头 chrome 中列出/杀死/...所有未决的承诺/异步事件?

Is it possible to list / kill / ... all pending promises / async events in a headless chrome?

我有一堆使用 headless chrome 的集成测试。因为在一个全新的配置文件上重新启动浏览器非常昂贵,所以 harness 在拆卸时尝试 "clean up" 浏览器状态(刷新缓存、清除 cookie 和存储,...)。

然而,有一个反复出现的问题,即在清理阶段,一些异步操作会解决并尝试在现在无意义的状态下执行它们所做的任何事情。

这里有两个问题:

  1. CDT 中的异步堆栈跟踪支持被列为实验性的,根本不会出现在响应中(可能是因为它们必须以某种方式通过隐藏标志启用)
  2. 我不知道那时候还有什么 运行,甚至无法真正调试由于 (1)
  3. 而导致的中断

有没有什么方法可以改善预期的情况,方法是在出现海森堡错误时进行拖网搜索,尝试通过更多的日志记录慢慢提高异步调用堆栈,直到找到根本原因?

首先我们制作一个钩子来捕获所有的xhr数据包。您必须在加载任何其他脚本之前执行此操作。可能在 运行 测试之前将其放入您的 boot/prepare 脚本中。

我在下面实现了一个开始和停止按钮。 start 发出 300 个 xhr 请求,只是 "normal" 方式。如果你按停止,你可以取消它们。理想情况下,您应该将停止事件处理程序代码放在 beforeunload 事件中。

如果您不想阻止它们,您可以分析它们的状态、请求的 URL 等...从一个整洁的数组中,您可以在代码中跟踪所有内容。

此示例有效,因为浏览器只能同时发出 "so" 多个请求。队列中的其余部分等待等待,直到有空位为止。我使用了 300 个请求,因为我不知道 large/slow 来源的请求不受 CORS 保护,这给了我们足够的时间来按下停止按钮(我希望)。

function addXMLRequestCallback(callback){
  var oldSend, i;
  if( XMLHttpRequest.callbacks ) {
      // we've already overridden send() so just add the callback
      XMLHttpRequest.callbacks.push( callback );
  } else {
      // create a callback queue
      XMLHttpRequest.callbacks = [callback];
      // store the native send()
      oldSend = XMLHttpRequest.prototype.send;
      // override the native send()
      XMLHttpRequest.prototype.send = function(){
          // process the callback queue
          // the xhr instance is passed into each callback but seems pretty useless
          // you can't tell what its destination is or call abort() without an error
          // so only really good for logging that a request has happened
          // I could be wrong, I hope so...
          // EDIT: I suppose you could override the onreadystatechange handler though
          for( i = 0; i < XMLHttpRequest.callbacks.length; i++ ) {
              XMLHttpRequest.callbacks[i]( this );
          }
          // call the native send()
          oldSend.apply(this, arguments);
      }
  }
}
/**
 * adding some debug data to the XHR objects. Note, don't depend on this, 
 * this is against good practises, ideally you'll have your own wrapper 
 * to deal with xhr objects and meta data.
 * The same way you can extend the XHR object to catch post data etc...
 */
var xhrProto = XMLHttpRequest.prototype,
    origOpen = xhrProto.open;
    origSend = xhrProto.send;
xhrProto.open = function (method, url) {
    this._url = url;
    return origOpen.apply(this, arguments);
};
xhrProto.send = function (data) {
    this._data = data;
    return origSend.apply(this, arguments);
};

+function() {
 var xhrs = [],
     i, 
     statuscount = 0, 
     status = document.getElementById('status'),
     DONE = 4;;
 addXMLRequestCallback((xhr) => {
    xhrs.push(xhr);
 });

 document.getElementById('start').addEventListener('click',(e) => {
    statuscount = 0;
    var data = JSON.stringify({
      'user': 'person',
      'pwd': 'password',
      'organization': 'place',
      'requiredkey': 'key'
    });
    for(var i = 0;i < 300; i++) {
       var oReq = new XMLHttpRequest();
       oReq.addEventListener("load", (e) => {
          statuscount++;
          status.value=statuscount;
       });
       oReq.open("GET", 'https://code.jquery.com/jquery-3.4.1.js');
       
       oReq.send(data);
    }
 });
 
 document.getElementById('cancel').addEventListener('click', (event) => {
     for(i = 0; i < xhrs.length; i++) {
         if(xhrs[i].readyState !== DONE) {
            console.log(xhrs[i]._url, xhrs[i]._data , 'is not done');
         }
         
     }
     /** Cancel everything */
     for(i = 0; i < xhrs.length; i++) {
        if(xhrs[i]) { 
           xhrs[i].abort();
        }
     }
     
 });
}();
<button id="start">start requests</button>
<button id="cancel">cancel requests</button>
<progress id="status" value="0" max="300"></progress>

addXMLRequestCallback 代码由 meouw from this answer
提供 xhrProto 代码保持调试变量礼貌 Joel Richard of from this answer