当前位置: 首页 > 后端技术 > Node.js

ARealAsync-AwaitExample

时间:2023-04-03 20:19:27 Node.js

译者注:通过真实的代码示例感受Async/Await的强大。原文:Async/await-Athoroughexample译者:Fundebug为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,转载仅供学习。现在Node.js8已经是LTS,我想是时候让大家尝试一下Async/Await特性了,它真的很有用!它可以帮助我们以同步的方式编写异步代码,大大提高了代码的可读性。在过去的2年里,Promise给我们带来了很多便利,但也让我们有一些失望。在这篇博客中,我将介绍一个真实的代码示例,它是一个RESTAPI控制器。通过展示我们如何从Promises切换到async/await,您将能够领略到Async/Await的魔力!Promise示例这是我工作项目中的真实控制器代码:constBPromise=require('bluebird');const{WrongCredentialsError,DBConnectionError,EmailError}=require('./../errors');/***以Express.js路由调用为例*/loginController({},{json:response=>console.log(response)},null)functionloginController(req,res,err){const{email,密码}=req;letuser;BPromise.try(()=>validateUserInput(req)).then(()=>fetchUserByEmail(email)).then(fetchedUser=>user=fetchedUser).then(()=>comparePasswords(req.password,user.密码)).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:'无效的电子邮件和/或密码'})).catch(EmailError,DBConnectionError,()=>res.json({success:false,error:'意外错误,请重试'})).catch(()=>res.json({success:false}))}/***验证来自请求的输入**@param{Object}input*@throws{WrongCredentialsError}*@returns{Void}*/functionvalidateUserInput(input){if(!input.email||!input.password){thrownewWrongCredentialsError();}}/***通过电子邮件从数据库中获取用户**@throwsWrongCredentialsError*@throwsDBConnectionError*@returns{BPromise}*/functionfetchUserByEmail(email){constuser={userId:'DUMMY_ID',email:'konmpar@gmail.com',password:'DUMMY_PASSWORD_HASH'}returnnewBPromise(resolve=>resolve(user));}/***比较两个密码**@param{String}inputPwd*@param{String}storedPwd*@throws{WrongCredentialsError}*@returns{Void}*/functioncomparePasswords(inputPwd,storedPwd){if(hashPassword(inputPwd)!==storedPwd){thrownewWrongCredential错误();}}/***哈希密码**@param{String}密码*@returns{String}*/functionhashPassword(password){returnpassword;}/***标记用户的登录时间戳**@param{String}userId*@throwsDBConnectionError*@returns{BPromise}*/functionmarkLoggedInTimestamp(userId){returnnewBPromise(resolve=>resolve());}/***发送后续电子邮件**@param{String}userId*@throwsEmailError*@returns{BPromise}*/functionsendEmail(userId){returnnewBPromise(resolve=>resolve());}/***生成一个JWT令牌发送给客户端**@param{Object}user*@returns{BPromise}*/functiongenerateJWT(user){consttoken='DUMMY_JWT_TOKEN';returnnewBPromise(resolve=>resolve(token));}一些值得注意的点:冗余外部变量letuser;/*...*/.then(fetchedUser=>user=fetchedUser)/*...*/.then(()=>sendEmail(user.userId))/*...*/可以看出user是一个全局变量,因为我需要在Promise链中使用如果不想定义多余的外部变量,还需要在Promise链中的每个函数中返回用户变量,这显然更糟糕。恼人的Promise链/*...*/BPromise.try(()=>validateUserInput(req))/*...*/Promise链必须以Promise开始,但是validateUserInput函数不返回Promise,所以你需要使用蓝鸟。我也知道这样写很奇怪……我讨厌Bluebird。我在很多地方都使用Bluebird。如果我不使用它,代码会更加臃肿。所谓DRY,也就是Don'trepeatyourself,我们可以使用Bluebird来尽可能简化代码。但是Bluebird是第三方依赖,万一出问题了怎么办?把Bluebird去掉应该更好!JavaScript太聪明(gui)和活(yi)了,有没有bug你也不知道,还不如连上Fundebug在线实时监控。Async/Await示例当我放弃Promise,使用Async/Await之后,代码是这样的:const{WrongCredentialsError,DBConnectionError,EmailError}=require('./../errors');/***EmulateanExpress.js以路由调用为例*/loginController({},{json:response=>console.log(response)},null)/****@param{Object}req*@param{Object}res*@param{Object}err*@returns{Void}*/asyncfunctionloginController(req,res,err){const{email,password}=req.email;try{if(!email||!password){thrownewWrongCredentialsError();}constuser=awaitfetchUserByEmail(email);if(user.password!==hashPassword(req.password)){thrownewWrongCredentialsError();}awaitmarkLoggedInTimestamp(user.userId);等待sendEmail(user.userId);consttoken=awaitgenerateJWT(user);res.json({成功:true,token});}catch(err){if(errinstanceofWrongCredentialsError){res.json({success:false,error:'无效的电子邮件和/或password'})}elseif(errinstanceofDBConnectionError||errinstanceofEmailError){res.json({success:false,error:'意外错误,请重试'});}else{res.json({success:false})}}}/***通过电子邮件从数据库中获取用户**@throwsWrongCredentialsError*@throwsDBConnectionError*@returns{Promise}*/functionfetchUserByEmail(email){constuser={userId:'DUMMY_ID',email:'konmpar@gmail.com',password:'DUMMY_PASSWORD_HASH'}returnnewPromise(resolve=>resolve(user));}/***哈希密码**@param{String}password*@returns{String}*/functionhashPassword(password){returnpassword;}/***标记用户的登录时间戳**@param{String}userId*@throwsDBConnectionError*@returns{Promise}*/functionmarkLoggedInTimestamp(userId){returnnewPromise(resolve=>resolve());}/***发送后续邮件**@param{String}userId*@throwsEmailError*@returns{Promise}*/functionsendEmail(userId){returnnewPromise(resolve=>resolve());}/***生成一个JWT令牌发送给客户端**@param{Object}user*@returns{Promise}*/functiongenerateJWT(user){consttoken='DUMMY_JWT_TOKEN';returnnewPromise(resolve=>resolve(token));}哈哈!!!没有外部变量现在,所有函数都在同一范围内调用,不再有.then函数。因此,我们不再需要定义冗余的全局变量,也不需要做冗余的变量赋值。没有多余的功能。Promise例子中的同步函数validateInput和comparePasswords的代码可以和异步函数一起写,所以不需要单独定义函数,代码更少。更高的可读性异步代码以同步的方式编写,同时减少了代码量,大大提高了可读性。不再需要Bluebird原生的Promise来替代Bluebird,也不再需要Bluebird的try方法。结论作为程序员,我们应该努力改进我们的代码。Async/Await可以带来很大的好处,帮助我们写出更具可读性的代码。如果您坚持使用Promises,请查看如何在Promise链中共享变量?.如果您对Async/Await感兴趣,请查看这些博客:Refactoring:FromPromisetoAsync/AwaitAsync/Await6ReasonstoReplacePromise这就是Async/Await简化JavaScript代码的方式作者Fundebug和本文地址:https://blog.fundebug.com/2018/01/31/a-real-async-await-example/