TL; DR - Если вы в течение последних нескольких лет занимались веб-разработкой, возможно, вы слышали дискуссии об обработке ошибок. «Используйте try / catch!», «Нет, используйте async / await / .catch!», «Нет, используйте promises / .catch!». Здесь представлены две новые функции, которые помогают разработчикам четко понимать статус подключения к данному сокету ввода-вывода и обрабатывать ошибки гораздо более четким и реактивным способом. С плагином, который берет на себя эту ответственность, и теперь, когда у разработчиков есть совершенно новая альтернатива и взгляд на проблему, мы надеемся, что вся дискуссия об обработке ошибок приглушится из-за устаревания основной предпосылки.

Отказ от ответственности: я являюсь автором 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 станет правдой, я могу легко стилизовать различные части страницы или компонента на основе этого (используя вычисляемые свойства). Кроме того, если я могу избавиться от необходимости проверки ответа внутри handleRespmethod, я также избавился от необходимости иметь тестовый пример для этого . Экономия времени может серьезно возрасти.

Статус подключения

Многие ошибки ввода-вывода можно проследить до фактического подключения к службе. Клиент вообще подключен? Это самый важный вопрос, который нужно задать, но его легко упустить. К счастью, socket.io-client предоставляет несколько событий, которые подключаемый модуль nuxt-socket-io может прослушивать, чтобы определить состояние если пользователь согласен на прослушивание (объяснено ниже). Следующие события:

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

Если желательно проверить статус, пользователь просто соглашается, определяя свойство socketStatus для того же компонента, который создает экземпляр this.$nuxtSocket. Затем плагин автоматически установит этот статус (он будет использовать версии имен событий в верблюжьем корпусе в качестве имен свойств, поскольку это общепринятое соглашение в Javascript). Если желательно использовать имя свойства, отличное от socketStatus, нужно просто установить свойство 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
  })
}

Для вашего удобства компонент SocketStatus.vue теперь также упакован с nuxt-socket-io, который поможет визуализировать статус: (import SocketStatus from ‘nuxt-socket-io/components/SocketStatus.vue)

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

Создадим следующие динамические таблицы:

Таким образом, если socketStatus реквизиты являются реактивными, это упрощает отображение или скрытие частей данной страницы в зависимости от статуса подключения.

Обработка ошибок

Ошибки ввода-вывода могут возникать даже при надежном соединении. Можно представить себе две основные категории ошибок: 1) тайм-аут и 2) ошибки, не связанные с тайм-аутом. Плагин позволяет пользователю использовать новые встроенные функции обработки ошибок.

1) Обработка ошибок тайм-аута. Ошибка тайм-аута может возникнуть, если клиент подключен, но делает неподдерживаемый запрос (запрос просто никогда не будет обработан). Пользователь разрешает плагину обрабатывать ошибки тайм-аута, указав emitTimeout (мс) в параметрах ввода-вывода при создании экземпляра this.$nuxtSocket:

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»), плагин не выдаст ошибку, а вместо этого установит свойство на компонент (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. Для серверной части рекомендуется также прикреплять к ответу сведения об ошибках, чтобы облегчить поиск и устранение неисправностей.

2) Обработка ошибок, не связанных с тайм-аутом, таких как неверные запросы или любые другие проблемы, связанные с серверной частью вашего приложения. Опять же, как и раньше, если this.emitErrors определен в компоненте, и ответ представляет собой объект с определенным свойством 'emitError', свойство this.emitErrors будет установлено в компоненте, в противном случае - 'emitError' будет брошен. Если требуется использовать другое имя для свойства emitErrors, это можно сделать, указав 'emitErrorsProp' в ioOptions:

data() {
  myEmitErrors: {} // Emit errors will get collected here now
}
mounted() {
  this.socket = this.$nuxtSocket({ 
    emitErrorsProp: 'myEmitErrors' 
  })
}

Половинное обещание

В начале статьи в одном из моих первых фрагментов кода упоминалось, что я хочу, чтобы пустой ответ на запрос рассматривался как ошибка. Это все еще то, что я хотел бы рассмотреть, однако на момент написания этой статьи плагин не рассматривал это как таковое. Он рассматривает только определенный resp.emitError как ошибку без тайм-аута. Я думаю, что сейчас для меня безопаснее предположить, что не все пользователи захотят, чтобы я обрабатывал их пустые ответы за них, поэтому я требую, чтобы они согласились описанным выше способом. Мне бы хотелось, чтобы достаточное количество людей захотело автоматизировать обработку пустых ответов, но сначала я хочу увидеть, как далеко люди продвинутся с кодом как есть, прежде чем встраивать в него что-то еще. Шаги малыша.

Вывод

В этой статье был рассмотрен совершенно другой и, надеюсь, более простой способ справиться со статусом и ошибками подключения ввода-вывода. Когда кажется, что жизнь предлагает нам лишь несколько способов решить проблему (попытаться / поймать или обещать / уловить), мне нравится думать об еще одном способе решить проблему с меньшими усилиями, когда это возможно. Плагин теперь включает и этот другой способ, и я надеюсь, что вы сочтете его полезным!