Как распараллелить несколько HTTP-вызовов с использованием RxJS - PullRequest
0 голосов
/ 18 сентября 2018

Справочная информация

Я создаю приложение Angular, в котором перечислены компании, зарегистрированные по данному адресу.

Чтобы дать некоторый контекст ...

Допустим, у меня есть x3 компании: Компания A, Компания B и Компания C.

Компания B зарегистрирована по тому же адресу, что и Компания A. Компания C не является.

Когда яперейдите к приложению и выполните фильтрацию для компании A, я ожидаю увидеть в списке только компанию B.

Проблема

Моя проблема не в том, что я не смог получитьэто работает, это просто слишком медленно!Мне нужно как-то использовать многопоточность / параллелизм.

Чтобы определить, какие компании зарегистрированы по данному адресу, мне нужно сделать несколько HTTP-вызовов.

Прежде чем объяснять последовательность, в которой ясделать HTTP-вызовы.Позвольте мне показать вам, как выглядит API:

GET /api/companies/CompanyA/address
{
  id: 1,
  addressLine1: '123 Some street',
  ...
}

GET /api/companies/CompanyA/links
[
  {id: 2, name: 'Company B'},
  {id: 3, name: 'Company C'},
]

Правильно, вот последовательность:

  1. Получите адрес для компании A и сохраните идентификатор
  2. Получить ссылки для компании A
  3. Цикл по каждой ссылке 3a.Получить адрес по ссылке 3б.Проверьте, соответствует ли идентификатор адреса идентификатору адреса компании А.Если это так, сохраните ссылку.

Текущая реализация

const companyAId: number = 1;    

const companyAAddress: Object = await this.httpService.getAddress(companyAId).toPromise();

const companyALinks: Object[] = await this.httpService.getLinks(companyAId).toPromise();

const companiesToShow: Object[] = [];
for (let link of links) {
    const linkAddress: Object = await this.httpService.getAddress(link ['id']).toPromise();
    if (linkAddress['id'] === companyAAddress['id']) companiesToShow.push(link );
}

Должно быть более элегантное / производительное решение!

Любая помощь будет принята с благодарностью:)

Спасибо,

Бен

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

Здесь следует рассмотреть другой макет.

Вариант 1

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

Вам потребуется только один вызов бэкэнда.И вы бы избежали await здесь, что делает тактильную также медленной для пользователя, если сервер не отвечает быстро.

псевдокод на стороне сервера:

// this is a new endpoint on the backend which holds your current client logic
on(/api/companies/<company>/address_links) {

  // retrieve the address with it's id like your first endpoint and store it temporary in address/id
  address = getCompanyAddress(company); 

  // query all all addresses with that id/address and store it in links[]
  other_companies[] = getCompaniesWithAddress(address);

  // Build your response JSON
  return_me = {
    id: address.id,
    addressLine1: address.line1,
    links: other_companies 
  }

  // send the response
  response.send(return_me);
}

Опция2

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

Веб-сокеты могут быть медленнее для отдельных запросов, если соединение еще не настроено, но имеет существенно меньшие издержки, чем несколько HTTP-запросов.Очень хорошая статья (от автора очень хорошей среды под названием FeathersJS) может быть найдена здесь: https://blog.feathersjs.com/http-vs-websockets-a-performance-comparison-da2533f13a77

Вы меняете код компонента для прослушивания Observer

this.subscription = this.myService.addressObserver$.subscribe((data: any) => {
  [Assign and do something]
}

иизменить службу для использования сокета (не полный и не работает, только в качестве примера):

import * as feathers from '@feathersjs/client';
import * as io from 'socket.io-client';
[...]

@Injectable()
export class MyService {
  private readonly feathersService: any;
  public myObserver$: Subject<any>;

  constructor() {
    const socket = io(<url>, {
      transports: ['websocket'],
      forceNew: true,
    });

    const feathersApp = feathers().configure(feathers.socketio(socket));
    this.feathersService = feathersApp.service('api/address');

    // on (create, delete, update) methods work like realtime when something changed on the server
    this.feathersService.on('updated', (address) => this.onUpdated(address));
  }

  // single find, can be adapted to query by something
  public find(): void {

    // the actual query to the server 
    this.feathersService.find(address).then((addresses: any) => {
      this.myObserver$.next(addresses.data);
    });
  }

  ...

Опция 3

сделать оба

0 голосов
/ 18 сентября 2018

Вы правы в том, что каждый из этих вызовов API должен ждать завершения другого вначале.

У вас нет доступа к многопоточности как таковой, но вы можете сделать это одновременно с помощью Promise.allкак ты это написал.В частности, вы можете получить адрес и ссылки одновременно, а затем все другие адреса одновременно.Я сократил некоторые имена вызовов методов, чтобы было легче писать:

const [companyAAddress, companyALinks] = await Promise.all(
  getAddress(id).toPromise(),
  getLinks(id).toPromise(),
);

const companiesToShow = await Promise.all(links.map(link => getAddress(link.id).pipe(
  map(linkAddress => [link, linkAddress]),
  filter(([, { id }]) => id === companyAAddress.id),
).toPromise())

Вместо того, чтобы использовать обещания, вы можете делать это строго с помощью Observables, что сделает его отменяемым.

forkJoin(getAddress(id), getLinks(id)).pipe(
  mergeMap(([companyAAddress, companyALinks]) => companyALinks.map(link => getAddress(link.id).pipe(
    map(linkAddress => [link, linkAddress]),
    filter(([, { id }]) => id === companyAAddress.id),
  ))
)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...