如何将 XMLHttpRequest 与主函数分开以获得更好的 visbility/testibility(无承诺/asnyc/await)

How to separate XMLHttpRequest from the main function for better visbility/testibility (without Promises / asnyc/await )

想象一下这个函数:

function myMainFunction() {
  doSomeInitialStuff();
  // more stuff..
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function () {
    if (xhr.readyState == XMLHttpRequest.DONE) {
      // Now that we know we received the result, we can do the heavy lifting here
      if (xhr.status == 200) {
        console.log("ready 200");
        let result = JSON.parse(xhr.responseText);
        doStuff(result);
        // and much more stuff..
      } else {
        console.log("error", xhr.status);
        return undefined;
      }
    }
  };

  xhr.open("GET", "http://example.com", true);
  xhr.send(null);
}

这个很好用,但是没法测试,这个功能成了怪物。 所以我想重构它,将所有不同的部分分离成它们自己独特的功能。
问题是,我不知道如何提取 XHR 部分并使其继续工作。
我不能使用 Promises 或 asnyc/await,必须坚持使用普通 XHR。
我通常做的是为 ajax 调用(或本例中的 xhr)创建一个单独的异步函数。只需等待它的结果并从那里开始。容易分离。但是这次我没有等待什么的奢侈。

我想表达的意思是这样的

function refactoredMyMainFunction() {
  doSomeInitialStuff();
  // more stuff..
  let result = xhrFunction(); 
  doStuff(result); // result would be undefined here, since I cannot wait for the xhr request to finish. 

}

您可以实施 callback-based API:

function myMainFunction() {
  doSomeInitialStuff();
  // more stuff..
  xhrFunction(doStuff);
}

function xhrFunction(cb) { 
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function () {
    if (xhr.readyState == XMLHttpRequest.DONE) {
      // Now that we know we received the result, we can do the heavy lifting here
      if (xhr.status == 200) {
        console.log("ready 200");
        let result = JSON.parse(xhr.responseText);
        cb(result);
        // and much more stuff..
      } else {
        console.log("error", xhr.status);
        return undefined;
      }
    }
  };

  xhr.open("GET", "http://example.com", true);
  xhr.send(null);
}