Node.js
7.6起, Node.js 搭載了有async
函數功能的V8引擎。當Node.js 8於10月31日成爲LTS版本後,咱們沒有理由不使用async
函數。接下來,我將簡要介紹async
函數,以及如何改變咱們編寫Node.js應用程序的方式。javascript
async
函數async
函數可讓你編寫基於Promise的代碼使他像同步的同樣。每當你用asnyc
關鍵字定義了一個函數體,你能夠在函數體內使用 await
關鍵字。當這個async
函數被調用,會獲得一個Promise
實例。當async
函數返回值時這個promise會執行。若是async
函數拋出錯誤,那麼會進入promise的rejected流程。java
await
關鍵字能夠用來等待Promise
進入 resolved
並有完成返回值。若是傳給await
的值不是Promise
實例,它會被轉爲 Promise
的 resolved
流程。node
const rp = require('request-promise'); async function main () { const result = await rp('https://google.com'); const twenty = await 20; // sleeeeeeeeping for a second await new Promise (resolve => { setTimeout(resolve, 1000); }); return result } main() .then(console.log) .catch(console.error);
async
函數若是你的Node.js應用已經使用了Promise
, 你只須要使用 await
替代Promise鏈式調用。若是你的代碼是基於 callback
, 遷移到 async
函數須要逐步修改現有代碼。能夠在新到功能中使用新的技術,若是必須保留舊有功能則可使用 Promise
進行簡單的包裝。爲此你可使用內置的util.promisify
(譯者注:Node.js 8.0+) 方法!git
const util = require('util'); const {readFile} = require('fs'); const readFileAsync = util.promisify(readFile); async function main () { const result = await readFileAsync('.gitignore'); return result } main() .then(console.log) .catch(console.error);
async
函數最佳實踐express
中使用async
函數As express supports Promises out of the box, using async functions with express is as simple as:github
express
是支持 Promise
的,因此使用async
函數能夠把代碼簡化爲:數據庫
const express = require('express'); const app = express(); app.get('/', async (request, response) => { // awaiting Promises here // if you just await a single promise, you could simply return with it, // no need to await for it const result = await getContent(); response.send(result); }); app.listen(process.env.PORT);
Edit1:如Keith Smith所指出的那樣,上面的例子有一個嚴重的問題 - 若是Promise
進入rejected
,express路由處理程序就會hang住,由於那裏沒有錯誤處理。express
要解決這個問題,你應該把你的異步處理程序封裝在一個處理錯誤的函數中:promise
const awaitHandlerFactory = middleware => { return async (req, res, next) => { try { await middleware(req, res, next) } catch (err) { next(err) } } } // and use it this way: app.get('/', awaitHandlerFactory(async (request, response) => { const result = await getContent(); response.send(result); }));
假設你正在作相似的事情,當一個操做須要兩個輸入,一個來自數據庫,另外一個來自外部服務:app
async function main () { const user = await Users.fetch(userId); const product = await Products.fetch(productId); await makePurchase(user, product); }
在這個case中,將會發生如下狀況:異步
正如所見,你能夠同時作前兩個操做,由於它們之間沒有依賴關係。 爲此應該使用 Promise.all
方法:
async function main () { const [user, product] = await Promise.all([ Users.fetch(userId), Products.fetch(productId) ]); await makePurchase(user, product); }
在某些狀況下,您只須要最快resolving
獲得Promise
的結果 - 在這種狀況時可使用Promise.race
方法。
參考下面的代碼
async function main () { await new Promise((resolve, reject) => { reject(new Error('💥')); }); }; main() .then(console.log);
若是運行這段代碼,你會在terminal上看到相似的消息:
(node:69738) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: 💥 (node:69738) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
在新版的Node.js中,若是Promise拒毫不會被處理,那麼會致使整個Node.js進程崩潰。 所以在必要時應該使用try-catch語句:
const util = require('util'); async function main () { try { await new Promise((resolve, reject) => { reject(new Error(' ')); }); } catch (err) { // handle error case // maybe throwing is okay depending on your use-case } } main() .then(console.log) .catch(console.error);
可是若是使用try-catch會丟失重要的異常如系統錯誤,那麼就要從新拋出異常。 要了解更多關於何時應該從新投擲的信息,我強烈建議閱讀Eran的Learning to Throw Again.。
Node.js的第一個異步控制流庫是由 Caolan McMahon
編寫的一async的異步控制流庫。 它提供了多個異步助手:
若是你不想從新造輪子,並且不想使用這個庫,那麼能夠重度使用 async
函數和 util .promisify
方法:
const util = require('util'); const async = require('async'); const numbers = [ 1, 2, 3, 4, 5 ]; mapLimitAsync = util.promisify(async.mapLimit); async function main () { return await mapLimitAsync(numbers, 2, (number, done) => { setTimeout(function () { done(null, number * 2); }, 100) }); }; main() .then(console.log) .catch(console.error);