使用具有回调函数的异步JavaScript尝试/捕获块

时间:2018-04-25 12:31:40

标签: javascript error-handling

我有一段代码:

update

在我的控制台中,我得到了

function backgroundReadFile(url, callback) {
  var req = new XMLHttpRequest();
  req.open("GET", url, true);
  req.addEventListener("load", function() {
    if (req.status < 400)
      callback(req.responseText);
  });
  req.send(null);
}
try {
  backgroundReadFile("example/data.txt", function(text) {
    if (text != "expected")
      throw new Error("That was unexpected");
  });
} catch (e) {
  console.log("Hello from the catch block");
}

这很好。但是,我被告知:

  

在代码中,异常不会被捕获,因为   致电Error: That was unexpected (line 13) 立即返回。然后控制离开   backgroundReadFile块,直到它才会调用它给出的函数   后面。

问题是:为什么其他错误不会被发现?当我们有连接问题,或文件不存在?据我所见,如果

,回调函数不会执行
try
例如,

未被触发。但它仍然存在 - 我仍然得到同样的错误 - req.addEventListener("load")

这是什么意思 - &#34;因为对Error: That was unexpected (line 13)的调用立即返回&#34;而不会被捕获异常?

谢谢。

2 个答案:

答案 0 :(得分:2)

您的backgroundReadFile函数包含两部分:同步部分和异步部分:

function backgroundReadFile(url, callback) {
  var req = new XMLHttpRequest();             // Synchronous
  req.open("GET", url, true);                 // Synchronous
  req.addEventListener("load", function() {   // Synchronous
    if (req.status < 400)                     // *A*synchronous
      callback(req.responseText);             // *A*synchronous
  });
  req.send(null);                             // Synchronous
}

您的try / catch在调用它时,该函数的同步部分引发的错误是非常正确的。

异步部分中的错误不会被try / catch捕获,因为有人告诉你,到那时控制流已经移动了

因此,如果它可能从同步代码中抛出,那么可能在该函数的调用周围有一个try / catch是完全合理的。

附注:如果您要使用回调样式,则应始终回调,以便使用您的函数的代码知道该过程已完成。一种方式是将err参数作为第一个参数传递给回调,如果没有错误则使用null,然后将任何数据作为第二个参数(这通常称为& #34; Node.js回调样式&#34;)。但在现代环境中,更好的选择是使用Promise。你可以用这样的最小改变来做到这一点:

function backgroundReadFile(url) {
  return new Promsie(function(resolve, reject) {
    var req = new XMLHttpRequest();
    req.open("GET", url, true);
    req.addEventListener("load", function() {
      if (req.status < 400) {
        resolve(req.responseText);
      } else {
        reject(new Error({status: req.status}));
    });
    req.addEventListener("error", reject);
    req.send(null);
  });
}

...你这样使用:

backgroundReadFile(url)
    .then(function(text) {
        // Use the text
    })
    .catch(function(err) {
        // Handle error
    });

但在XMLHttpRequest的特定情况下,您可以使用fetch代替,它已经为您提供了承诺:

function backgroundReadFile(url) {
  return fetch(url).then(response => {
      if (!response.ok) {
          throw new Error({status: response.status});
      }
      return response.text();
  });
}

答案 1 :(得分:1)

以下是对代码中发生的情况的逐步细分。

  1. 代码进入try-catch块。
  2. backgroundReadFile被调用,有两个参数:"example/data.txt"和匿名函数。
  3. backgroundReadFile创建一个AJAX请求并调用send()。这里是异步概念发挥作用的地方:实际的HTTP请求是立即发送而不是,而是放在一个队列中,一旦浏览器运行完它正在运行的任何代码就执行当下(即你的try-ctach块)。
  4. backgroundReadFile已经完成了。执行返回到try-catch块。
  5. 没有遇到异常,因此跳过了catch块。
  6. 包含try-catch块的代码已完成执行。 现在浏览器可以继续执行队列中的第一个异步操作,这是您的AJAX请求。
  7. 发送HTTP请求,一旦收到响应,就会触发onload事件处理程序 - 无论响应是什么(即成功或错误)。
  8. 您传递给backgroundReadFile的匿名函数作为onload事件处理程序的一部分调用,并抛出Error。但是,正如您现在所看到的,您的代码在try-catch块中不再是而不是,因此它不会被捕获。
  9. TL; DR:抛出错误的函数是在try-catch块中定义,但之外执行。

    此外,AJAX请求中的错误处理有两个方面:连接错误和服务器端错误。连接错误可以是请求超时或发送请求时可能发生的一些其他随机错误;这些可以分别在ontimeoutonerror事件处理程序中处理。但是,如果HTTP请求进入服务器并收到响应,则就XMLHttpRequest而言,请求成功。例如,您可以检查status的{​​{1}}属性(其中包含HTTP响应代码,例如200表示“确定”,404表示“未找到”等),并确定它是否成功。