使用承诺编写清洁代码

在本文中,我们将看看在Node.js中使用promise的一些示例,并讨论了一次处理多个并行promise的内容。

使用承诺编写清洁代码

在询问“在网络上”时,我发现新手在开始使用Node.js时遇到的主要问题之一是“Promises”。他们发现他们很难理解,很难知道如何处理他们以及何时正确使用他们。

在本视频中,我给出了他们如何工作以及如何创建它们的基本解释:

现在,在这篇文章中,我想深入一些,看一些其他的例子,并讨论像一次处理多个并行承诺的事情。

承诺允许你做的一件事就是清理你的代码(这是它们允许你基本完成的唯一事情之一)。但他们这样做的方式是向您提供所需的语法糖来声明您在做什么,而不是真正关注逻辑的控制流。

他们以一种非常基本的方式来做到这一点,请注意,不是我们明确地处理功能性或反应性程序设计(虽然我们很可能),但它是朝着这个方向迈出的一步。为什么?因为您正在逐步构建您的代码和逻辑,特别是在处理多个级别的复杂性时。

我来给你展示:

authenticateUser(usrname, pwd, (err, isAuth) => {

if(err) return dealWithYourErrors(err);

if(!isAuth) return dealWithUnauthorizedAccess(usrname);

getSessionToken(usrname, (err, token) => {

if(err) return dealWithYourErrors(err);

loadUserDetails(usrname, (err, details) => {

if(err) retun dealWithYourErrors(err);

let user = new User(usrname, token, details);

performAction(user, (err, result) => { //this is what you wanted to do all along

if(err) return dealWithYourErrors(err);

sendBackResponse(result);

})

})

})

})

以上是嵌套回调的典型示例,其中有几条信息需要从不同的服务中获取(或由于其他逻辑而采用不同的步骤),默认情况下,回调仅允许您按顺序处理异步行为,在这种情况下,这并不理想。双方 getSessionToken 并 loadUserDetails 可以并行完成,因为它们不需要彼此的结果来执行自己的操作。但是这样做需要您添加额外的代码来处理并行支持或添加第三方库。

此外,从某种意义上说,整个结构非常重要,它明确说明如何处理错误以及如何处理串行调用。您(开发人员)需要在编写这些步骤时考虑这些步骤,以确保正确的行为。

让我告诉你一个基于承诺的方法将如何看待:

authenticateUser(username, pwd)

.then( preActions )

.then( performAction )

.catch(dealWithYourErrors)

我敢肯定,我们都可以同意,写和读更简单。让我向您展示这些函数的模拟实现,因为承诺需要在所有函数中返回:

function preActions(usrname) {

return Promise.all([getSessionToken(usrname), loadUserDetails(usrname)])

}

function authenticateUser(usr, pwd){

return new Promise( (resolve, reject) => {

resolve(usr);

})

}

function getSessionToken(usrname) {

return new Promise( (resolve, reject) => {

resolve("11111")

})

}

function loadUserDetails(usrname) {

return new Promise( (resolve, reject) => {

resolve({name: 'Fernando'})

})

}

function performAction() {

console.log(arguments)

}

function dealWithYourErrors(err) {

console.error(err)

}

为了测试目的,这些功能已经被简化了,但是,欢迎您随意弄乱它们以获得不同的结果。您甚至可以尝试拒绝某些承诺,以查看流量何时会中断。

进一步解释一下:

  • preActions使用all本地Promise对象的方法 并行调用两个函数 。如果他们中的任何一个失败(因此拒绝他们各自的承诺),那么整个集合将失败并且该 catch 方法将被调用。
  • 其他人只是简单地回复承诺,正如上面的视频所解释的那样。

该对象允许您处理多个承诺的另一种方式是使用该 方法,该方法不是试图解决所有承诺,而是等待第一个承诺完成,并根据承诺是否失败或成功已被解决或被拒绝。

何时使用种族?

当这种方法派上用场时可能并不明显,因为当你有多个异步调用时,你可能需要所有这些调用的结果。但有些情况下它派上用场。

例如,如果性能是平台的重要组成部分,那么您可能需要拥有数据源的多个副本,并且您可以根据网络流量或其他外部因素查询它们,以期获得最快速度的副本。

你可以毫无保留地做到这一点,但同样,这种方法会增加费用,因为你必须处理逻辑以了解谁先返回,以及如何处理其他待处理的请求。

通过承诺和race 方法,您可以专注于从所有来源获取数据,并让JavaScript处理剩余的数据。

您可能想要考虑使用此方法的另一个示例是,在尝试决定是否在UI中显示加载指示符时。在创建SPA时,一个好的经验法则是您的异步调用应该触发用户的加载指示符,让他/她知道发生了什么事情。但是,当底层请求发生得非常快时,这种规则并不理想,因为您可能在用户界面中获得的所有内容都是闪烁的消息,而且速度过快。加载时间可能取决于太多的事情,您可以创建规则以了解何时显示该指标,以及何时仅在没有它的情况下执行请求。

你可以玩弄拒绝和解析的概念来得到类似这样的东西:

function yourAsynchronousRequest(params) {

return new Promise((resolve, reject) => {

//here is your request code, it'll resolve once it gets the actual data from the server

});

}

function showDataToUser(params) {

return yourAsynchronousRequest(params).then( data => console.log("data fetched:", data));

}

function timeout() {

return new Promise((resolve, reject) => {

setTimeout(() => reject(), TIMEOUTLIMIT); //TIMEOUTLIMIT is a constant you configured

});

}

function showLoadingIndicator() {

console.log("please wait...")

}

Promise.race([showDataToUser(), timeout()]).catch(showLoadingIndicator);

现在比赛是针对一个实际的异步请求,并且简单地将超时设置为限制器。现在决定是否显示加载指示符的逻辑隐藏在race 方法后面 。


分享到:


相關文章: