传统的错误处理方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function  thisThrows (    throw  new  Error ("Thrown from thisThrows()" ); } try  {    thisThrows (); } catch  (e) {     console .error (e); } finally  {     console .log ('We do cleanup here' ); } 
非常简单,不过多赘述
一个包装在Async中的Try Catch 现在我们修改这个程序,把thisThrows()函数标记为async。此时抛出的错误,实际上相当于抛出一个Reject。一个Async函数,总是返回一个Promise
当没有定义返回语句的时候,函数运行结束后实际返回的是Promise,相当于return Promise.Resolve() 
当有定义返回语句的时候,相当于返回了一个带有值的Promise,相当于return Promise.Resolve('My return String') 
当抛出错误的时候,相当于return Promise.Reject(error) 
 
看下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 async  function  thisThrows (    throw  new  Error ("Thrown from thisThrows()" ); } try  {    thisThrows (); } catch  (e) {     console .error (e); } finally  {     console .log ('We do cleanup here' ); } 
thisThrows返回一个Reject,所以我们使用常规的try...catch无法正常的捕捉到错误。
thisThrws标记为async,所以我们调用的时候,代码不会等待,finally块会先执行,所以这里无法捕捉到错误。
有两个方式可以解决这个问题:
第一个解决方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 async  function  thisThrows (    throw  new  Error ("Thrown from thisThrows()" ); } async  function  run (    try  {         await  thisThrows ();     } catch  (e) {         console .error (e);     } finally  {         console .log ('We do cleanup here' );     } } run ();
第二个解决方式 1 2 3 4 5 6 7 8 9 10 11 12 async  function  thisThrows (    throw  new  Error ("Thrown from thisThrows()" ); } thisThrows ()    .catch (console .error )     .then (() =>  console .log ('We do cleanup here' )); 
async/await的方式相对来说更容易理解。
注意点 从async函数中返回 考虑下下面代码会输出什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 async  function  thisThrows (    throw  new  Error ("Thrown from thisThrows()" ); } async  function  myFunctionThatCatches (    try  {         return  thisThrows ();     } catch  (e) {         console .error (e);     } finally  {         console .log ('We do cleanup here' );     }     return  "Nothing found" ; } async  function  run (    const  myValue = await  myFunctionThatCatches ();     console .log (myValue); } run ();
我们可能期待输出
1 2 We do cleanup here Nothing Found 
实际输出一个UnhandledPromiseRejection
我们分析下
thisThrows() 是异步方法; 
异步方法中抛出了一个错误,实际返回的是Promise.Reject 
myFunctionThatCatches中返回了这个Promise.Reject 
外部是以await标记的,发现是一个Reject的Prmoise,所以抛出unlandled promise rejection 
 
我们可以在返回中增加await解决这个问题(第七行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 async  function  thisThrows (    throw  new  Error ("Thrown from thisThrows()" ); } async  function  myFunctionThatCatches (    try  {         return  await  thisThrows ();      } catch  (e) {         console .error (e);     } finally  {         console .log ('We do cleanup here' );     }     return  "Nothing found" ; } async  function  run (    const  myValue = await  myFunctionThatCatches ();     console .log (myValue); } run ();
重置stack trace 在代码中,经常会看到有人捕获错误并将其包装在一个新的错误中,就像下面的代码片段中一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 function  thisThrows (    throw  new  Error ("Thrown from thisThrows()" ); } function  myFunctionThatCatches (    try  {         return  thisThrows ();     } catch  (e) {         throw  new  TypeError (e.message );     } finally  {         console .log ('We do cleanup here' );     } } async  function  run (    try  {         await  myFunctionThatCatches ();     } catch  (e) {         console .error (e);     } } run ();
注意我们的堆栈跟踪仅从我们捕获原始异常的地方开始。当我们在 2 行创建错误并在 9 行捕获它时,我们会丢失原始的堆栈跟踪,因为我们现在创建了一个新的 TypeError 类型的错误,只保留原始的错误消息(有时我们甚至都不保留)。
如果 thisThrows() 函数中有更多的逻辑,在该函数的某个地方抛出了一个错误,我们在记录的堆栈跟踪中看不到问题的起源,因为我们创建了一个新的错误,它将生成一个全新的堆栈跟踪。如果我们只是重新抛出原始错误,我们就不会遇到这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 function  thisThrows (    throw  new  Error ("Thrown from thisThrows()" ); } function  myFunctionThatCatches (    try  {         return  thisThrows ();     } catch  (e) {                  throw  e;     } finally  {         console .log ('We do cleanup here' );     } } async  function  run (    try  {         await  myFunctionThatCatches ();     } catch  (e) {         console .error (e);     } } run ();
堆栈跟踪现在指向实际错误的起源,即我们脚本的第 2 行。
处理错误时要意识到这个问题是很重要的。有时这可能是可取的,但通常这会掩盖问题的来源,使得调试问题的根源变得困难。如果你为包装错误创建自定义错误,请确保跟踪原始的堆栈跟踪,以免调试变成一场噩梦。
总结 
我们可以使用 try...catch 来处理同步代码。 
我们可以使用 try...catch (与 async 函数结合使用)和 .catch() 方法来处理异步代码的错误。 
在 try 块中返回一个promise时,如果你希望 try...catch 块捕获错误,请确保 await 它。 
在包装错误并重新抛出时,请注意,您会丢失带有错误来源的堆栈跟踪。