博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一个真实的Async/Await示例
阅读量:5995 次
发布时间:2019-06-20

本文共 6132 字,大约阅读时间需要 20 分钟。

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

  • 原文:
  • 译者:

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

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

这边博客,我将介绍一个真实的代码示例,它是一个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
} */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你也不知道,不妨接入线上实时监控

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
} */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,不妨看看。

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

版权声明:

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

你可能感兴趣的文章
分布式中Redis实现Session终结篇
查看>>
excel随机数的获取
查看>>
项目管理生命周期各个阶段的文档
查看>>
注册团队博客地址
查看>>
寒假挑战PythonTip(一人一python)总结——算法是程序的灵魂,程序员的心法
查看>>
PHP基础面试题(1-10)
查看>>
点分治
查看>>
ElasticSearch之Centos7下安装
查看>>
Bootstrap3 表格-带边框的表格
查看>>
数据库中大的事物
查看>>
基于SOA架构---ServiceProxy定义
查看>>
CodeIgniter开发实际案例-新闻网站【转】
查看>>
WP7->网络->读取网页源码
查看>>
兵以诈立——我读孙子
查看>>
Python3 类和继承和组合
查看>>
JAVA操作MYSQL数据库
查看>>
条款10 令operator=返回一个reference to *this
查看>>
子串判断
查看>>
消息队列的四大典型使用场景
查看>>
深入浅出 JavaScript 数组 v0.5
查看>>