摘要:如果不希望定义多余的外层变量,则需要在链中的每一个函数中都返回变量,这样做显然更加糟糕。
译者按: 通过真实的代码示例感受Async/Await的力量。
原文: Async/await - A thorough example
译者: Fundebug
为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。
既然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}
*/
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}
*/
function generateJWT(user) {
const token = "DUMMY_JWT_TOKEN";
return new Promise(resolve => resolve(token));
}
哈哈!!!
没有外层变量现在,所有函数都在同一个作用域中调用,不再需要.then函数。因此,我们不再需要定义多余的全局变量,也不需要做多余的变量赋值。
没有多余的函数Promise示例中的同步函数validateInput和comparePasswords的代码可以与异步函数写在一起,因此可以不再需要定义多带带的函数,代码更少。
可读性更高异步代码采用同步方式来写,同时减少了代码量,可读性大大提高。
不再需要Bluebird原生的Promise可以替代Bluebird,且不再需要Bluebird的try方法了。
结论作为程序员,我们应该努力完善代码。Async/Await可以带来很大好处,帮助我们写出可读性更高的代码。如果你坚持使用Promise,不妨看看如何在Promise链中共享变量?。
如果你对Async/Await感兴趣的话,可以看看这些博客:
重构:从Promise到Async/Await
Async/Await替代Promise的6个理由
Async/Await是这样简化JavaScript代码的
版权声明:
转载时请注明作者Fundebug以及本文地址:
https://blog.fundebug.com/2018/01/31/a-real-async-await-example/
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/107433.html
摘要:版本以及之前,本身还没有异步执行代码的能力,宿主环境传递给引擎,然后按顺序执行,由宿主发起任务。采纳引擎术语,把宿主发起的任务称为宏观任务,把引擎发起的任务称为微观任务。基本用法示例的回调是一个异步的执行过程。 笔记说明 重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时间开的一个专栏,每天10分钟,重构你的前端知识体系,笔者主要整理学习过程的一些要点笔记以及感悟,完整的...
摘要:版本以及之前,本身还没有异步执行代码的能力,宿主环境传递给引擎,然后按顺序执行,由宿主发起任务。采纳引擎术语,把宿主发起的任务称为宏观任务,把引擎发起的任务称为微观任务。基本用法示例的回调是一个异步的执行过程。 笔记说明 重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时间开的一个专栏,每天10分钟,重构你的前端知识体系,笔者主要整理学习过程的一些要点笔记以及感悟,完整的...
摘要:版本以及之前,本身还没有异步执行代码的能力,宿主环境传递给引擎,然后按顺序执行,由宿主发起任务。采纳引擎术语,把宿主发起的任务称为宏观任务,把引擎发起的任务称为微观任务。基本用法示例的回调是一个异步的执行过程。 笔记说明 重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时间开的一个专栏,每天10分钟,重构你的前端知识体系,笔者主要整理学习过程的一些要点笔记以及感悟,完整的...
摘要:一方面,这里替代的是异步代码的编写方式,并非完全抛弃大家心爱的,地球人都知道是基于的,不用太伤心另一方面,是基于回调函数实现的,那也没有替代回调函数咯重构代码之后,我仍然用到了库。 摘要: 夸张点说,技术的发展与历史一样,顺之者昌,逆之者亡。JS开发者们,赶紧拥抱Async/Await吧! GitHub仓库: Fundebug/promise-asyncawait 早在半年多之前,...
阅读 1468·2021-10-09 09:43
阅读 20164·2021-09-22 15:52
阅读 1387·2019-08-30 15:44
阅读 3413·2019-08-30 15:44
阅读 3517·2019-08-26 14:07
阅读 1147·2019-08-26 13:55
阅读 2868·2019-08-26 13:41
阅读 3372·2019-08-26 13:29