译者按: 通过真实的代码示例感受Async/Await的力量。

为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。

既然Node.js 8已经LTS了,我想大家是时候试一试Async/Await特性了,真的很好用!它可以帮助我们用同步的方式写异步代码,极大地提高了代码的可读性。在过去的2年时间里,Promise给我们带来了不少便利,同时也让我们有一些失望。

这边博客,我将介绍一个真实的代码示例,它是一个REST API的controller。通过展示我们如何从Promise切换到async/await,你讲能够体会到Async/Await的神奇之处!

Promise示例

下面是我的工作项目中真实的Controller代码:

const BPromise = require('bluebird');

const { WrongCredentialsError, DBConnectionError, EmailError } = require('./../errors');

/**
* Emulate an Express.js route call as an example
*/
loginController({}, { json: response => console.log(response) }, null) function loginController (req, res, err) {
const { email, password } = req; let user; BPromise.try(() => validateUserInput(req))
.then(() => fetchUserByEmail(email))
.then(fetchedUser => user = fetchedUser)
.then(() => comparePasswords(req.password, user.password))
.then(() => markLoggedInTimestamp(user.userId))
.then(() => sendEmail(user.userId))
.then(() => generateJWT(user))
.then(token => res.json({ success: true, token }))
.catch(WrongCredentialsError, () => res.json({ success: false, error: 'Invalid email and/or password' }))
.catch(EmailError, DBConnectionError, () => res.json({ success: false, error: 'Unexpected error, please try again' }))
.catch(() => res.json({ success: false }))
} /**
* Validate input from Request
*
* @param {Object} input
* @throws {WrongCredentialsError}
* @returns {Void}
*/
function validateUserInput(input) {
if (!input.email || !input.password) {
throw new WrongCredentialsError();
}
} /**
* Fetch a User from the DB by Email
*
* @throws WrongCredentialsError
* @throws DBConnectionError
* @returns {BPromise}
*/
function fetchUserByEmail(email) {
const user = {
userId: 'DUMMY_ID',
email: 'konmpar@gmail.com',
password: 'DUMMY_PASSWORD_HASH'
}
return new BPromise(resolve => resolve(user));
} /**
* Compare two password
*
* @param {String} inputPwd
* @param {String} storedPwd
* @throws {WrongCredentialsError}
* @returns {Void}
*/
function comparePasswords(inputPwd, storedPwd) {
if (hashPassword(inputPwd) !== storedPwd) {
throw new WrongCredentialsError();
}
} /**
* Hash password
*
* @param {String} password
* @returns {String}
*/
function hashPassword(password) {
return password;
} /**
* Mark a user's logged in timestamp
*
* @param {String} userId
* @throws DBConnectionError
* @returns {BPromise}
*/
function markLoggedInTimestamp(userId) {
return new BPromise(resolve => resolve());
} /**
* Send a follow up email
*
* @param {String} userId
* @throws EmailError
* @returns {BPromise}
*/
function sendEmail(userId) {
return new BPromise(resolve => resolve());
} /**
* Generate a JWT token to send to the client
*
* @param {Object} user
* @returns {BPromise<String>}
*/
function generateJWT(user) {
const token = 'DUMMY_JWT_TOKEN'; return new BPromise(resolve => resolve(token));
}

一些值得注意的要点:

多余的外层变量

let user;

/* ... */
.then(fetchedUser => user = fetchedUser)
/* ... */
.then(() => sendEmail(user.userId))
/* ... */

可知,user是一个全局变量,因为我需要在Promise链中使用它。如果不希望定义多余的外层变量,则需要在Promise链中的每一个函数中都返回user变量,这样做显然更加糟糕。

烦人的Promise链

/* ... */
BPromise.try(() => validateUserInput(req))
/* ... */

一个Promise链必须从Promise开始,但是validateUserInput函数并没有返回Promise,这时需要使用Bluebird。我也知道这样写比较奇怪…

讨厌的Bluebird

我在很多地方都使用了Bluebird,如果不用它的话,代码会更加臃肿。所谓DRY,即Don’t repeat yourself,我们可以使用Bluebird去尽量简化代码。但是,Bluebird是一个第三方依赖,如果出问题了怎么办?去掉Bluebird应该更好!

JavaScript太灵(gui)活(yi)了,出了BUG你也不知道,不妨接入Fundebug线上实时监控

Async/Await示例

当我放弃Promise,使用Async/Await之后,代码是这样的:

const { WrongCredentialsError, DBConnectionError, EmailError } = require('./../errors');

/**
* Emulate an Express.js route call as an example
*/
loginController({}, { json: response => console.log(response) }, null) /**
*
* @param {Object} req
* @param {Object} res
* @param {Object} err
* @returns {Void}
*/
async function loginController(req, res, err) {
const { email, password } = req.email; try {
if (!email || !password) {
throw new WrongCredentialsError();
} const user = await fetchUserByEmail(email); if (user.password !== hashPassword(req.password)) {
throw new WrongCredentialsError();
} await markLoggedInTimestamp(user.userId);
await sendEmail(user.userId); const token = await generateJWT(user); res.json({ success: true, token }); } catch (err) {
if (err instanceof WrongCredentialsError) {
res.json({ success: false, error: 'Invalid email and/or password' })
} else if (err instanceof DBConnectionError || err instanceof EmailError) {
res.json({ success: false, error: 'Unexpected error, please try again' });
} else {
res.json({ success: false })
}
}
} /**
* Fetch a User from the DB by Email
*
* @throws WrongCredentialsError
* @throws DBConnectionError
* @returns {Promise}
*/
function fetchUserByEmail(email) {
const user = {
userId: 'DUMMY_ID',
email: 'konmpar@gmail.com',
password: 'DUMMY_PASSWORD_HASH'
}
return new Promise(resolve => resolve(user));
} /**
* Hash password
*
* @param {String} password
* @returns {String}
*/
function hashPassword(password) {
return password;
} /**
* Mark a user's logged in timestamp
*
* @param {String} userId
* @throws DBConnectionError
* @returns {Promise}
*/
function markLoggedInTimestamp(userId) {
return new Promise(resolve => resolve());
} /**
* Send a follow up email
*
* @param {String} userId
* @throws EmailError
* @returns {Promise}
*/
function sendEmail(userId) {
return new Promise(resolve => resolve());
} /**
* Generate a JWT token to send to the client
*
* @param {Object} user
* @returns {Promise<String>}
*/
function generateJWT(user) {
const token = 'DUMMY_JWT_TOKEN'; return new Promise(resolve => resolve(token));
}

哈哈!!!

没有外层变量

现在,所有函数都在同一个作用域中调用,不再需要.then函数。因此,我们不再需要定义多余的全局变量,也不需要做多余的变量赋值。

没有多余的函数

Promise示例中的同步函数validateInputcomparePasswords的代码可以与异步函数写在一起,因此可以不再需要定义单独的函数,代码更少。

可读性更高

异步代码采用同步方式来写,同时减少了代码量,可读性大大提高。

不再需要Bluebird

原生的Promise可以替代Bluebird,且不再需要Bluebird的try方法了。

结论

作为程序员,我们应该努力完善代码。Async/Await可以带来很大好处,帮助我们写出可读性更高的代码。如果你坚持使用Promise,不妨看看如何在Promise链中共享变量?

如果你对Async/Await感兴趣的话,可以看看这些博客:

版权声明:
转载时请注明作者Fundebug以及本文地址:
https://blog.fundebug.com/2018/01/31/a-real-async-await-example/

一个真实的Async/Await示例的更多相关文章

  1. Async/Await 学习与示例

    参考:Async/await学习 es 7 提供了对 promise 对象的更好的操作,省去了很多丧心病狂的链式异步请求,promise 是回调地狱的福音,而 Async/Await 则是 promi ...

  2. [.NET] 利用 async & await 的异步编程

    利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html  目录 异步编程的简介 异 ...

  3. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  4. [C#] 走进异步编程的世界 - 开始接触 async/await

    走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $&qu ...

  5. ASP.NET 中的 Async/Await 简介

    本文转载自MSDN 作者:Stephen Cleary 原文地址:https://msdn.microsoft.com/en-us/magazine/dn802603.aspx 大多数有关 async ...

  6. EntityFramework 如何进行异步化(关键词:async·await·SaveChangesAsync·ToListAsync)

    应用程序为什么要异步化?关于这个原因就不多说了,至于现有项目中代码异步化改进,可以参考:实际案例:在现有代码中通过async/await实现并行 这篇博文内容针对的是,EntityFramework ...

  7. 走进异步编程的世界 - 开始接触 async/await

    [C#] 走进异步编程的世界 - 开始接触 async/await   走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async ...

  8. ASP.NET 上的 Async/Await 简介

    原文链接 大多数有关 async/await 的在线资源假定您正在开发客户端应用程序,但在服务器上有 async 的位置吗?可以非常肯定地回答“有”.本文是对 ASP.NET 上异步请求的概念性概述, ...

  9. Python PEP 492 中文翻译——协程与async/await语法

    原文标题:PEP 0492 -- Coroutines with async and await syntax 原文链接:https://www.python.org/dev/peps/pep-049 ...

随机推荐

  1. 「ZJOI2017」树状数组(二维线段树)

    「ZJOI2017」树状数组(二维线段树) 吉老师的题目真是难想... 代码中求的是 \(\sum_{i=l-1}^{r-1}a_i\),而实际求的是 \(\sum_{i=l}^{r}a_i\),所以 ...

  2. IEDA的程序调试debug

    以前只是浅层面的使用dubug来查看程序运行顺序,排查一些异常的原因, 今天由于要学习一些源码,所以系统的记录一下(借鉴网上资料总结而来) 主要涉及到的功能区为如下: A::重启项目 快捷键 Ctrl ...

  3. 网络编程懒人入门(九):通俗讲解,有了IP地址,为何还要用MAC地址?

    1.前言 标题虽然是为了解释有了 IP 地址,为什么还要用 MAC 地址,但是本文的重点在于理解为什么要有 IP 这样的东西.本文对读者的定位是知道 MAC 地址是什么,IP 地址是什么. (本文同步 ...

  4. Javascript高级编程学习笔记(35)—— DOM(1)节点

    DOM JS由三部分组成 1.BOM 2.DOM 3.ECMAScript ES和BOM在前面的文章已经介绍过了 今天开始JS组成的最后一部分DOM(文档对象模型) 我们知道,JS中的这三个部分实际上 ...

  5. TS - 问题分析与处理的一般性方法

    本文是对解决问题的一些方法内容的改写与补充! 1 接触与了解 从总体着眼,从细节入手! 确认基本相关信息是必须执行的首要环节,也是后续处理问题的基础. 如果无法清楚地辨别或陈述问题的基本信息,那么,此 ...

  6. 【code block】局部代码块+构造代码块+静态代码块

    1.局部代码块 位置:位于类的方法中 表示方法:{} 作用:控制变量的生命周期,减少内存消耗 demo: public class LocalCode { public static void mai ...

  7. spring boot -thymeleaf-异常处理

    异常流程 :controller-ControllerAdvice-异常解析器-默认的error页面 1.错误页面error.html: 程序出现异常会自动跳到error.html里,视频效果,该页面 ...

  8. [原创]K8飞刀Final

    法律声明: 工具仅供安全研究或授权渗透,非法用途后果自负. 工具: K8飞刀Final作者: K8哥哥博客: https://www.cnblogs.com/k8gege简介: 一款多功能网络安全渗透 ...

  9. mongo 更新字段值,若不存在则自动创建

    查询语句 db.getCollection("A表").update( {  "id":{$eq:11} } ,{     $set:{"a" ...

  10. javascript数组的属性、方法和清空-最全!!!(必看)

    今天经理要我从新看一遍js,当我再看<精通js和jquery>这本书时,发现关于数组的这章节讲的很少,于是想自己总结一下数组的常用方法. 定义数组: var arr = new Array ...