原文來自: blog.grossman.io/how-to-writ…javascript
在ES7的中,咱們可使用async & await進行編寫異步函數,使用這種寫法咱們的異步函數看起來就跟同步代碼同樣。java
在以前的版本(ES6),可使用Promise寫法,來簡化咱們異步編程的流程,同時也避免了回調地獄。node
回調地獄是語義化產生的一個術語,它的釋義能夠用下面這種狀況進行闡述:git
function AsyncTask() { asyncFuncA(function(err, resultA){ if(err) return cb(err); asyncFuncB(function(err, resultB){ if(err) return cb(err); asyncFuncC(function(err, resultC){ if(err) return cb(err); // And so it goes.... }); }); }); } 複製代碼
上例代碼中, 不斷的回調,使得代碼維護和管理控制流程變得十分的困難。 咱們不妨考慮下這種狀況,假如某個if語句須要執行其餘的方法,而回調函數FunctionA的結果爲foo。github
ES6和Promise的出現,使得咱們能夠簡化以前"回調地獄"般的代碼以下:數據庫
function asyncTask(cb) { asyncFuncA.then(AsyncFuncB) .then(AsyncFuncC) .then(AsyncFuncD) .then(data => cb(null, data) .catch(err => cb(err)); } 複製代碼
這樣編寫是否是看起來舒服多了?npm
可是在實際的業務場景中,異步流的處理可能會更加複雜一些。舉例來講,編程
假如在你的一個(node.js)服務器中,你可能想要:數組
在使用了Promise語法後,這樣固然看起來更加的簡潔了,可是,在我看來仍然有一點混亂。promise
您須要使用轉譯器才能使用Async/Await,您可使用babel插件或Typescript來添加所需的工具。
此時, 若使用 async/await, 你會發現代碼寫起來舒服多了. 它容許咱們像下面同樣編寫代碼:
async function asyncTask(cb) { const user = await UserModel.findById(1); if(!user) return cb('No user found'); const savedTask = await TaskModel({userId: user.id, name: 'Demo Task'}); if(user.notificationsEnabled) { await NotificationService.sendNotification(user.id, 'Task Created'); } if(savedTask.assignedUser.id !== user.id) { await NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'); } cb(null, savedTask); } 複製代碼
上面的代碼看起來可讀性加強了很多, 可是如何處理錯誤報錯呢?
在執行異步任務使用Promise的時候可能會發生一些錯誤相似數據庫鏈接出錯,數據庫模型驗證錯誤等狀況。
當一個異步函數正在等待Promise返回值的時候,當Promise方法報了錯誤的時候,它會拋出異常,這個異常能夠在catch方法裏面捕獲到。
在使用Async/Await時,咱們一般使用try/catch語句進行異常捕獲。
try{ //do something } catch{ // deal err } 複製代碼
我沒有編寫強類型語言的背景,所以增長額外的try/catch
語句, 對我來講增長了額外的代碼,這在我看來很是的冗餘不乾淨。 我相信這多是我的喜愛的緣由,但這是我對此的見解。
因此以前的代碼看起來像這樣:
async function asyncTask(cb) { try { const user = await UserModel.findById(1); if(!user) return cb('No user found'); } catch(e) { return cb('Unexpected error occurred'); } try { const savedTask = await TaskModel({userId: user.id, name: 'Demo Task'}); } catch(e) { return cb('Error occurred while saving task'); } if(user.notificationsEnabled) { try { await NotificationService.sendNotification(user.id, 'Task Created'); } catch(e) { return cb('Error while sending notification'); } } if(savedTask.assignedUser.id !== user.id) { try { await NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'); } catch(e) { return cb('Error while sending notification'); } } cb(null, savedTask); } 複製代碼
最近我一直在使用go-lang
進行編碼,而且很是喜歡他們的解決方案,它的代碼看起來像這樣:
data, err := db.Query("SELECT ...") if err != nil { return err } 複製代碼
我認爲它比使用try/catch
語句塊更加簡潔,而且代碼量更少,這使得它可讀和可維護更好。
可是使用Await的話,若是沒有爲其提供try-catch處理異常的話,當程序發生錯誤的時候,它會默默的退出(你看到不拋出的異常)。假如你沒有提供catch語句來捕捉錯誤的話,你將沒法控制它。
當我和Tomer Barnea(個人好朋友)坐在一塊兒並試圖找到一個更簡潔的解決方案時,咱們獲得了下一個使用方法: 請記住: Await在等待一個Promise返回值
有了這些知識,咱們就能夠製做一個小的通用函數來幫助咱們捕捉這些錯誤。
// to.js export default function to(promise) { return promise.then(data => { return [null, data]; }) .catch(err => [err]); } 複製代碼
這個通用函數接收一個Promise,而後將處理成功的返回值以數組的形式做爲附加值返回,而且在catch方法中接收到捕捉到的第一個錯誤。
import to from './to.js'; async function asyncTask(cb) { let err, user, savedTask; [err, user] = await to(UserModel.findById(1)); if(!user) return cb('No user found'); [err, savedTask] = await to(TaskModel({userId: user.id, name: 'Demo Task'})); if(err) return cb('Error occurred while saving task'); if(user.notificationsEnabled) { const [err] = await to(NotificationService.sendNotification(user.id, 'Task Created')); if(err) return cb('Error while sending notification'); } cb(null, savedTask); } 複製代碼
上面的例子只是一個使用該解決方案的簡單用例,你能夠在io.js中添加攔截方法(相似調試的斷點),該方法將接收原始錯誤對象,打印日誌或者進行其餘任何你想要進行的操做,而後再返回操做後的對象。
咱們爲這個庫建立了一個簡單的NPM包(Github Repo),您可使用如下方法進行安裝:
npm i await-to-js
複製代碼
這篇文章只是尋找Async/Await功能的一種不一樣方式,徹底基於我的意見。 您可使用Promise,僅使用try-catch和許多其餘解決方案來實現相似的結果。 只要你喜歡而且它適用。
async/await 結合promise使用就行了, 即異步流程控制的最後加上 promise的catch.
async function task(){ return await req(); } task().catch(e => console.error(e)) 複製代碼
async function hello(flag){ return new Promise((resolve, reject) => { if(flag) setTimeout(() => resolve('hello'), 100); else reject('hello-error'); }) } async function demo(flag){ return new Promise((resolve, reject) => { if(flag) setTimeout(() => resolve('demo'), 100); else reject('demo-error'); }) } async function main(){ let res = await hello(1).catch(e => console.error(e)); console.log('res => ', res); let result = await Promise.all([hello(1), demo(1)]); // let result = await Promise.all([hello(1), demo(0)]).catch(e => console.error('error => ', e)); console.log('result => ', result); } main() 複製代碼