Использование абстракции — наблюдателя в javaScript программировании

в 9:47, , рубрики: javascript, абстракции, метки: ,

В функциональном программировании мы редко заботимся о примитивах параллелизма, таких как потоки. Вместо этого мы используем различные абстракции, делающие наш код более легким для понимания. В JavaScript наблюдатель – абстракция, которую мы встречаем чаще всего. Она обычно ассоциируется с AJAX – запросами. Мы часто используем абстракции для рассуждения о нашем параллельном коде, но эти абстракции могут применяться и в других ситуациях. Высокоуровневые пользовательские интерфейсы, по существу, это большое количество асинхронных событий, которые могут быть представлены, как таковые, в коде.

Рассмотрим на примере. Мы работаем над дизайном сервиса, осуществляющего хранение видеозаписей. Пользователи могут как покупать понравившиеся им видеозаписи, так и просматривать личное видео. Если они подписываются… им нужен значок для покупки видео, и мы можем использовать диалоговые окна для предоставления возможности покупки и покупательного процесса.

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


Создание наблюдателей при пользовательском взаимодействии.
В любое время мы можем иметь дело с пользовательским взаимодействием, которое, по своей сути, является асинхронным. В большинстве случаев все начинается и заканчивается с объявления событий на отдельные ДОМ — элементы. Это может быть лучше абстракцией, если мы думаем о сложных интерфейсах, с которыми будут взаимодействовать пользователи и представлять, что это будет одна асинхронная операция в коде.

Пользователи могут быть авторизованы или не авторизованы. Мы могли бы отслеживать это, выставив нечто вроде флажка, который будет всплывать во всем коде. Однако, в большинстве случаев, если пользователь не авторизован, мы только подталкиваем его к тому, что бы он прошел авторизацию. Это означает, что вместо пользователя, который может пройти авторизацию, а может и нет, мы получаем пользователя, который может быть авторизован в будущем. Давайте посмотрим, как мы могли бы представить это в коде:

class UsSes
  construct: (@Q, @InputModals, @Session) ->
    @_session = @Session.get()

  signIn: ->
    @_authDeferred = @Q.defer()
    @_bayOrPrompt()
    @_authDeferred.promise

  utterSignIn: (data) ->
    @Session.save(data).then(@_utterAuth)

  _resolveOrPrompt: ->
    if @_session?.signedIn
      @_utterAuth(@_session)
    else
      @AuthModals.openSignIn()
  _utterAuth: (session) ->
    @_session = session
    @_authDeferred.resolve(session.user)

Теперь немного о том, что здесь происходит. C – это библиотека Криса Коваля, предназначенная для работы с наблюдателями. Мы не будем говорить о реализации InputModals и Session. Мы только предположим, что InputModals вызывает модальное окно и что либо может вызвать utterSignIn, когда форма авторизации отправляется. Session делает 2 вещи. Она спрашивает сервер, если мы уже прошли авторизацию, и отправляет AJAX запрос на сервер, возвращая наблюдатель с объектом сессии.
Когда вызывается signIn, мы возвращаем наблюдатель, который, в конечном счете, будет создан для текущего пользователя. Если пользователь уже прошел авторизацию, мы создаем наблюдатель немедленно. Иначе, мы открываем модельное окно для прохождения пользователем авторизации и создаем наблюдатель однажды, при отправке формы.
Хорошие паттерны создаются, что бы быть реализованными единажды.
Это невероятно важно. Мы полностью убираем из кода необходимость беспокоиться о том, действительно ли пользователь был авторизован, почти во всех случаях. Мы можем создать его однажды, и использовать в дальнейшем ту же концепцию для покупок.

class UserBays
  construct: (@Q, @UsSes, @BayModal) ->

  bay: (@video) =>
    @UsSes.input ().then(@_promiseBay (video))

  totalBay: (license)
    @user.addLicense(@video, license)
    @_purchaseDeferred.resolve(license)

  _promisePurchase: (video) => (@user) =>
    @_bayDeferred = @Q.defer()
    @_resolveOrPrompt(video)
    @_bayDeferred.promise

  _resolveOrPrompt: (video) =>
    if @user.his (video)
      @utterBay (@user.licenseFor(video))
    else<br />
      @ BayModal.openBayForm(video)

Мы применили тот же паттерн здесь. Если у пользователя уже есть собственное видео, мы создаем наблюдателя с правами немедленно. Если нет, мы предлагаем пользователю купить его и создать наблюдатель когда лицензия будет получена позже. Пользователь может купить только 1 видео за раз. Если пользователь закрывает модальное окно и пытается купить еще что-нибудь, старые права могут быть удалены.
Наконец, мы можем скрыть все это внутри объекта, который отвечает за проигрывание видео:

class VideoPlayer
  constructor: (@UserPurchases, @PlayerModal, @Video) ->

  play: (video) =>
    @UserPurchases.purchase(video).then(@_loadAndPlay(video))

  _loadAndPlay: (video) => (license) =>
    @Video.load(video, license).then(@PlayerModal.openPlayer)

Выше код, в котором для проигрывания видео нужно только вызвать функцию VideoPlayer.play. Ему не нужно заботиться о себе, независимо от того, прошел ли пользователь авторизацию, или от того, если у пользователя свой контент. Нам бы до сих пор нужно было бы иметь флаг внутри шаблона, что бы решить, показывать кнопку “Купить” или “Проигрывать”. Однако, если мы используем Null Object pattern и передаем шаблону Guest, когда пользователь не авторизован, это все еще остается сравнительно просто.

Размышляя о взаимодействии с пользователем более высокого уровня, мы так же могли бы подумать о большем количестве асинхронных вычислений, и быть в состоянии изолировать друг от друга такие вещи, как авторизация и покупка контента в одном месте. Проверка каждого из этих объектов так же становится более простой, чем иметь дело все всеми случаями в одном месте.

Такие ситуации встречаются повсюду, если вы присмотритесь. Необходимо помнить, что асинхронность применима не только для параллелизма, и создается не только для XHR.

Автор: borovoyyura

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js