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);