В настоящее время я создаю приложение, которое будет использовать WebRTC, чтобы упростить общение между несколькими пользователями одновременно. Он построен с использованием Angular 7 на интерфейсе и Node на сервере. Когда я начал его создавать, я выбрал Angular для интерфейса. Я уже создавал приложения с помощью Vue и React, поэтому хотел увидеть Angular в действии. Я читал сообщения в блогах о различиях между ними и понял, что для этого проекта мне нужно что-то лучше, чем Vue и React. Платформа, которую можно обслуживать, поддерживает создание приложения с повторно используемыми модулями или виджетами, хорошо поддерживается и документируется. Я прочитал документацию по Angular, поигрался с кодом и в конце концов выбрал Angular для своего следующего проекта.

Около месяца назад я все еще изучал Angular и rxjs, а также лучшие практики, как справляться с вещами (и я все еще изучаю). Проблема заключалась в том, что мне пришлось сделать два HTTP-вызова в одном методе. Второй вызов зависел от ответа первого, поэтому я не мог использовать forkJoin, combineLatest или merge из rxjs или любых других комбинированных операторов, потому что все они работают таким образом, что немедленно подписываются на любой предоставленный наблюдаемый объект. Мне нужно было, чтобы второй наблюдаемый подписывался только после завершения первого, и с данными из ответа первого.

Что у меня было на практике, так это регистрация пользователя и его вход после того, как он зарегистрировался, в одном и том же действии. На бэкэнде эти два действия разделены, но они не обязательно должны быть разделены на внешнем интерфейсе.

Я мог бы создать единый маршрут к серверу, который включал бы оба действия, но мне не нравится объединять два разных действия в одно только потому, что это было бы проще для клиента. Такое соединение API может привести к будущим ошибкам в том смысле, что если какое-либо из двух действий изменится, то то, которое обрабатывает их оба, также должно быть изменено. В случае использования, когда у меня может быть микросервисная архитектура и система обмена сообщениями, такая как RabbitMQ, событие complete_sign_up было бы в порядке, чтобы предотвратить условия гонки, когда несколько служб полагаются на порядок событий, но это не было моим вариантом использования.

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

Функция выглядела примерно так

completeAuth(userData) {
    userRepository.signUp(userData).subscribe((err, res) => {
        // manipulate response here

        const data = res.data;
        userRepository.signIn(data).subscribe((err, res) => {
            // auth done, inform the client code
        });
    });
}

Этот код также может быть асинхронным/ожидающим, но существует много устаревшего кода с большим количеством адских обратных вызовов, и рефакторинг для асинхронного/ожидающего может оказаться невозможным.

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

userService.completeUserAuth(user)
        .on('done', (result) => {
           
        })
        .on('signUpError', () => {

        })
        .on('signInError', () => {

        });
completeUserAuth(userData) {
    const em = new EventEmitter();
    
    em.emit('authStarted', userData);
    userRepository.signUp(userData).subscribe((err, res) => {
        if (err) {
            em.emit('signInError', err);
          
            return;
        }
        
        em.emit('signUpComplete', res);
        const data = res.data;
        userRepository.signIn(data).subscribe((err, res) => {
            if (err) {
                em.emit('signUpError', err);

                return;
            }
            
            em.emit('signUpComplete', res);
            em.emit('done', res);
            // auth done, inform the client code
        });
    });

    return em;
}

Единственной проблемой было использование фактического генератора событий. Я нашел эту публикацию в Stack Overflow, которая привела меня к этой библиотеке. И в этой библиотеке было именно то, что мне было нужно. Единственная проблема заключалась в том, что мне не нравится использовать всю библиотеку, какой бы короткой она ни была, только для одного варианта использования. Или два, если уж на то пошло. Я всегда стремлюсь выжать из браузера максимум эффективности, а установка библиотеки хоть и с нулевыми зависимостями, но с 500 строками кода — это не то, что я хотел. Поэтому я только что создал EventEmitter, который отлично подходил для моего собственного варианта использования.

export default class EventEmitter {
  private listeners = {};

  on(event: string, callback: Function) {
    if (!this.listeners.hasOwnProperty(event)) {
      this.listeners[event] = [];
    }

    this.listeners[event].push(callback);

    return this;
  }

  emit(event: string, ...data: any) {
    if (!this.listeners.hasOwnProperty(event)) {
      return null;
    }

    for (let i = 0; i < this.listeners[event].length; i++) {
      const callback = this.listeners[event][i];

      callback.call(this, ...data);
    }
  }
}

2 функции, 25 строк кода. И его копирование/вставка в другие проекты. Вы можете сказать, что копирование/вставка — это не возможность повторного использования. И это правда. Но для таких простых случаев, как этот, я думаю, что это вполне нормально. Пакеты меняют версии, код добавляется и подвергается рефакторингу, и все ваши приложения должны быть обновлены до последней возможной версии кода. Но зачем засорять ваш каталог node_modules, когда вы можете полностью контролировать код с помощью простых 25 строк кода? Думайте об этом как о функции add(a, b), которая складывает два числа. Не могли бы вы сделать это пакетом npm?

Сводка

Это был мой первый блог. Я надеюсь, что вам понравилось. Не стесняйтесь комментировать, что это был самый красивый фрагмент кода и текста, который вы когда-либо видели (умышленный сарказм).