小七平時在使用ES2017的 async功能經常會有如下:
const bluebird = require('bluebird'); async function doSomething() { await bluebird.delay(1000); throw new Error('ttt'); } (async function() { return doSomething();// 關注點 })() .then(function() { console.log('ok'); }) .catch(function(err) { console.error('fail'); });
小七在調用doSomething的時候直接使用 `return doSomething()` ,而不是用 `return await doSomething()`。
因為它們的執行和結果都是一樣的。在大部分情況下,這種方式是正確的,而且代碼也比較簡潔。
但事實上在執行的時候是有些差異的,我們看下下面的例子。
(async function() { try { return doSomething();// 關注點,這里我們省略了await 產生了更我們設想不太一樣的結果 } catch (err) { console.log('do something ignore'); } })() .then(function() { console.log('ok'); }) .catch(function(err) { console.error('fail'); }); //輸出 :fail
小七這里原本的設想是 在調用doSomething的時候,如果有什么錯誤的話,忽略錯誤,正常返回。
但是結果確實拋出了錯誤,被最后面的catch捕獲。輸出了fail。
於是調整了下代碼:
const bluebird = require('bluebird'); async function doSomething() { await bluebird.delay(1000); throw new Error('ttt'); } (async function() { try { return await doSomething();// 關注點,這里恢復了省略掉的 await } catch (err) { console.log('do something ignore'); } })() .then(function() { console.log('ok'); }) .catch(function(err) { console.error('fail'); }); //輸出 : //do something ignore //ok
把 await 恢復回來就正常了。
這里主要的原因就在於小七對async語法糖原理的誤解,小七以為在async函數中使用return的時候和return await是一樣的,是因為return 隱含了await的功能。然而並非如此,async中的return 只是簡單的返回一個promise,所以return 在使用的時候並沒有任何拋錯,try catch 自然就沒法獲得該錯誤。而返回的promise被后面的.catch方法捕獲到錯誤。
而如果使用 return await doSomething() 的時候,等價於 先await了doSomething返回的promise,如果有reject,則會直接傳給cacth block 處理。
總結,async函數中的return 並沒有黑魔法,在大部分情況下也不需要黑魔法,因為async方法的結果也是一個promise,所以返回一個promise是等價的。
但我們還是要理解它的實現原理。
因為在async 中 try catch 語法糖的原理是處理同步拋出的錯誤和await產生的reject,所以,我們不能省略掉await的調用。