TL;DR - 如果您过去几年一直从事 Web 开发,您可能听说过关于错误处理的争论。 “使用 try / catch!”,“不,使用 async / await / .catch!”,“不,使用 promises / .catch!”。这里介绍了两个新功能,可帮助开发人员清楚地了解给定 IO 套接字的连接状态,并以更清晰、更被动的方式处理错误。随着插件承担了这一责任,现在开发人员对手头的问题有了全新的替代方案和观点,希望由于底层前提的过时,关于错误处理的整个辩论会变得平淡。

免责声明:我是作者nuxt-socket-io


简介

除非你是机器人先生,他让他的代码第一次运行,否则当你的代码尝试从后端或其他服务请求数据时,你很可能会遇到错误。最有可能的是,您的代码如下所示:

try { 
  const resp = await Svc.getData({ userId: 'abc123' })
  if (resp !== undefined) { // Note: Please don't do this. 
    // If it's undefined, it's an error if you were expecting a response.
    /* handle response */
  }
} catch (err) {
  /* handle error */ // this placeholder comment stays here forever
  throw new Error(err) // Note: Please don't do this! 
  // ^^ Don't catch an error just to throw it!)
} 

这两个代码块看起来都非常简单且有些优雅,但是当您有许多不同类型的请求要发送时,问题很快就会变得一团糟。在你意识到之前,你的代码会被许多 try/catch 块弄得乱七八糟。考虑到 VueJS 为我们提供了响应式属性,并允许我们创建计算属性,这些属性会随着其他属性的变化而变化,我认为我们可以做得更好!

这是我的观点。当我调用一些方法来获取数据时,这些是我的期望:

// I want my request to be simple: (i.e., just make the request)
Svc.getData(...) // I just want to call this and have the response get sent directly to a property "resp".

// Success handling: (if all was good, handle response)
function handleResp(resp) { // If I want to post-process resp, I call this
  /* handle resp */
  // The response is valid here, if not...
  // I have no business calling this function
}

// Error handling: (if errors occurred, collect them and don't set property "resp")
emitErrors: { // <-- send any errors directly to this property
  getData: [{...}], // <-- send specific getData errors here
  // it's useful to include hints and timestamps
}

这样,我可以分离我的关注点并保持我的代码完全有条理。如果emitErrors变得真实,我可以轻松地根据它设置页面或组件的不同部分的样式(使用_计算属性_)。另外,如果我可以消除在handleResp方法中对响应进行_validating_ 的需要,我_也_ 消除了为此提供_test case_ 的需要。节省的时间会大大增加。

连接状态

许多 IO 错误可以追溯到服务的实际连接。客户端是否已连接?这是最基本的问题,但很容易被忽视。幸运的是,socket.io-client 公开了几个事件,nuxt-socket-io 插件可以监听这些事件以确定用户选择监听的状态_if_(解释如下)。以下事件是:

const clientEvts = [
  'connect_error', 
  'connect_timeout',
  'reconnect',
  'reconnect_attempt',
  'reconnecting',
  'reconnect_error',
  'reconnect_failed',
  'ping',
  'pong'
]

如果需要检查状态,用户只需通过在实例化this.$nuxtSocket的同一组件上定义属性socketStatus来选择加入。然后_plugin_ 将自动设置该状态(它将使用事件名称的驼峰式版本作为prop 名称,因为这是 Javascript 中的常见约定)。如果希望使用socketStatus以外的 prop 名称,只需设置 ioOpts 属性statusProp

例子:

data() {
  return {
    socketStatus: {}, // simply define this, and it will be populated with the status
    badStatus: {} // Status will be populated here if "statusProp == 'badStatus'"
  }
},
mounted() {
  this.goodSocket = this.$nuxtSocket({
    name: 'goodSocket',
    channel: '/index',
    reconnection: false
  })

  this.badSocket = this.$nuxtSocket({
    name: 'badSocket',
    channel: '/index',
    reconnection: true,
    statusProp: 'badStatus' // This will cause 'badStatus' prop to be populated
  })
}

为方便起见,现在还使用 nuxt-socket-io 打包了一个 SocketStatus.vue 组件,这将有助于可视化状态:

<socket-status :status="socketStatus"></socket-status>
<socket-status :status="badStatus"></socket-status>

将产生以下动态表:

[Alt](https://res.cloudinary.com/practicaldev/image/fetch/s--0J3GySQV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev. s3.amazonaws.com/i/c79o9eus84jwykxq5dkl.png)

因此,由于 socketStatus 道具是反应式的,它可以很容易地根据连接状态显示或隐藏给定页面的某些部分。

错误处理

即使连接牢固,仍然可能发生 IO 错误。两个主要类别的错误可以被认为是:1)超时和2)非超时相关。该插件允许用户利用新的内置错误处理功能。

  1. 处理超时错误。如果客户端已连接但发出不受支持的请求(该请求永远不会得到处理),则可能会发生超时错误。用户通过在实例化this.$nuxtSocket时在 IO 选项中指定emitTimeout(ms) 来选择让插件处理超时错误:
this.socket = this.$nuxtSocket({ channel: '/examples', emitTimeout: 1000 }) // 1000 ms

然后,如果发生“emitTimeout”,则有两种可能的结果。一种是,插件的方法将拒绝并出现“emitTimeout”错误,由用户在下游捕获错误:

this.someEmitMethod() 
.catch((err) => { // If method times out, catch the err
  /* Handle err */
})

以上允许用户以已经熟悉的方式编写代码,但是,我认为还有一种更简单的方法来处理错误。

该插件可以提供一种完全不同的错误处理方式,具体取决于用户是否允许。如果用户在组件上定义属性“emitErrors”并且服务器响应附加错误(即,具有定义属性“emitError”的对象),则插件不会抛出错误,而是将属性设置为 on组件 (this.emitErrors) 并通过错误的发射事件组织this.emitErrors。这可能会产生更简洁的代码,并且可以更容易地使用组件的计算属性,当“emitErrors”属性更改时,这些属性将发生更改:

data() {
  emitErrors: {} // Emit errors will get collected here, if resp.emitError is defined
}
...
this.someEmitMethod() // Now, when this times out, emitErrors will get updated (i.e., an error won't be thrown)

重要说明:为了更新this.emitErrors,服务器必须将其错误响应作为对象发送回,并定义属性“emitError”。建议后端也将错误详细信息附加到响应中,以帮助进行故障排除。

  1. 处理非超时错误,例如错误请求或任何特定于应用程序后端的错误。同样,像以前一样,如果在组件中定义了this.emitErrors,并且响应是具有定义属性“emitError”的对象,则属性this.emitErrors将在组件上设置,否则,将抛出“emitError”。如果希望为 emitErrors 属性使用不同的名称,可以通过在 ioOptions 中指定“emitErrorsProp”来实现:
data() {
  myEmitErrors: {} // Emit errors will get collected here now
}

mounted() {
  this.socket = this.$nuxtSocket({ emitErrorsProp: 'myEmitErrors' })
}

一个半满的承诺

在文章的开头,我的第一个代码片段之一提到了我希望如何将空响应视为错误。这仍然是我想考虑的事情,但是,在撰写本文时,插件并没有这样对待它。它仅将定义的resp.emitError视为非超时错误。我认为现在假设不是所有用户都希望我为他们处理他们的空响应对我来说更安全,这就是为什么我要求他们以上述方式选择加入。如果有足够多的人想要自动化的空响应处理,我会喜欢它,但我首先想看看人们在构建更多代码之前能在多大程度上按原样使用代码。婴儿步。

结论

本文回顾了一种完全不同的、并且希望更简单的方法来处理 IO 连接状态和错误。当生活似乎只为我们提供了几种解决问题的方法(try/catch vs. promise/catch)时,我喜欢想出另一种方法来尽可能少地解决问题。该插件现在包含其他方式,希望对您有所帮助!

Logo

前往低代码交流专区

更多推荐