finally 容易被忽视的几个知识点

远子 •  2021年03月05日

JS 中的 try ... catch 有三种写法:

第一种:

try {
  // your code
} catch (error) {
  
}

第二种:

try {
  // your code
} finally {
  
}

第三种:

try {
  // your code
} catch (error) {
  
} finally {
  
}

catch 中代码报错的时

来看以下代码:

function test() {
  try {
    notExistFunction();
    console.log("1");
  } catch (error) {
    console.log("2");
    throw error;
  } finally {
    console.log("3");
  }
  console.log("4");
}

test();

输出如下:

image-20210305104807872

我们可以发现:

  • notExistFunction() 抛错以后,后续代码 console.log(1) 不会执行;
  • 在 catch 中抛出错误,finally 代码块里的 console.log(3) 会执行;
  • 在 catch 中抛出错误,整个 try...catch...finally 代码块的后续代码 console.log(4) 不会执行;

结论:无论 try...catch 里是否报错,finally 代码块都会执行。当 try...catch 抛错时,try...catch...finally 后续代码不会执行。

同理,try...finally 下边这种不处理 error 的写法表现一致:

function test() {
    try {
    notExistFunction();
  } finally {
    console.log(1);
  }
  console.log(2);
}

test();

finally 中有 return 语句时

来看以下代码:

function test1() {
  try {
    return 1;
  } catch (error) {
    return 2;
  } finally {
    return 3;
  }
  return 4;
}

function test2() {
  try {
    notExistFunction();
    return 1;
  } catch (error) {
    return 2;
  } finally {
    return 3;
  }
  return 4;
}

function test3() {
  try {
    notExistFunction();
    return 1;
  } catch (error) {
    throw error;
    return 2;
  } finally {
    return 3;
  }
  return 4;
}

console.log('test1:', test1()); // 输出:3
console.log('test2:', test2()); // 输出:3
console.log('test3:', test3()); // 输出:3

以上三个函数的输出结果都是 3:

image-20210305105650465

结论:如果从 finally 代码块中返回了一个值,那么这个值将称为整个 try...catch...finally 的返回值,无论是否有 return 语句在 trycatch 中。

在循环中使用

来看以下代码:

for (let i = 0; i < 5; i++) {
  try {
    notExistFunction();
  } finally {
    console.log(i, 'finally');
  }
  console.log(i, 'after finally');
}
console.log('done');

输出如下:

image-20210305111435935

结论:try 代码块抛错会阻断 try...catch...finally 后续代码,并且终止循环。

嵌套 try...catch

来看以下代码:

function test() {
  try {
    try {
      notExistFunction();
    } catch (error) {
      console.log("inner catch called");
    } finally {
      console.log("inner finally called");
    }
  } catch (error) {
    console.log("outer catch called");
  } finally {
    console.log("outer finally called");
  }
}

test();

输出如下:

image-20210305112344901

结论:

  1. 异常代码 notExistFunction() 只会被 inner catch 代码块捕获,不会被 outer catch 代码块捕获;
  2. 异常代码只会被离他最近的 catch 代码块捕获一次。

当然,如果 inner catch 代码块抛错的话,将被 outer catch 代码块捕获:

function test() {
  try {
    try {
      notExistFunction();
    } catch (error) {
      console.log(error.message);
            throw new Error("Error from inner")
    }
  } catch (error) {
      console.log(error.message);
  }
}

test();

输出如下:

image-20210305112756345

总结

  1. 无论如何(无论 try 代码块和 catch 代码块是否抛错),finally 代码块一定会执行;
  2. try 代码块抛错,或 catch 代码块抛错时,try...catch...finally 的后续代码不会执行;
  3. finally 代码块的 return 将作为 try...catch...finallyreturn

(完)