Jeer
@Jeer
уверенный пользователь

Как писать асинхронные js функций в html?

Всем привет!
Как известно, есть несколько способов работы с асинхронными функциями в js. Например, ангуляр построен на библиотеке rxjs, которая использует паттерн Observable с его .subscribe. Так же в js в одной из последних спецификаций появился синтаксис async/await, основанный на промиссах. И мне нравится писать именно в таком виде. Более лаконично что ли.
К примеру, пишем api вызов вот так:
public async getUserRoles(userId: string) {
    return this.get<number[]>('GetRoles?userId=' + userId).toPromise();
  }

И далее можем вызывать в каких-то компонентах
this.userRoles = await this.service.getUserRoles(this.userId);


Далее, я хочу запилить прослойку. Сложить вызов какого-то апи вызова в локальное хранилище в глобальный сервис. Чтобы не дёргать апишку каждый раз. Например, список существующих пользователей:

export class UsersApiService extends BaseApiService {
  private storage: UserProfileModel[] = null;

  constructor(public http: HttpClient) {
    super('Users', http);
  }

  public async GetProfiles() {
    if (!this.storage) {
      this.storage = (await this.get<any[]>('GetProfiles').toPromise()).map(x => new UserProfileModel(x));
    }

    return this.storage;
  }

  public async getUser(userId: string) {
    const profiles = await this.GetProfiles();
    return profiles.find(x => x.id == userId);
  }

  public async getUserRoles(userId: string) {
    return this.get<number[]>('GetRoles?userId=' + userId).toPromise();
  }

  public async setUserRoles(userId: string, roles: number[]) {
    return this.post('SetRoles', { userId: userId, roles: roles }).toPromise();
  }
}

Хранилище storage, как мы тут видим, уже обычное, без промиссов и прочего. Обычный массив. Логика следующая: когда вызывается метод GetUser, этот метод (через await) получает массив всех пользователей. То есть, первый раз загружается через апи, но, если он уже содержит какой-то результат, апишка не дёргается, просто find по массиву отдаёт требуемого пользователя.
Для справки приведу так же класс UserProfileModel. В этой моделе присутствует вычисляемый геттер shortName
/** Пользователь */
export class UserProfileModel {
  /** GUID */
  public id: string;

  /** Логин */
  public userName: string;

  /** Эмейл */
  public email?: string;

  /** Телефон */
  public phone?: string;

  constructor(obj: any = {}) {
    this.id = obj.id;
    this.email = obj.email;
    this.phone = obj.phone;
    this.userName = obj.userName;
  }

  get shortName(): string {
    return this.userName || this.email || this.phone;
  }
}

Проблем нет, разумеется, если я вызываю функцию GetUser в какой-либо асинхронной функции в обработчике .ts компонента. Но, если же я пытаюсь получить GetUser внутри html какого-либо компонента, совершенно ничего не получается. Даже если я использую pipe async. К примеру, отображаю список модели Issue, в которой есть поле с идентификатором пользователя:
<tr *ngFor="let el of dataSource">
        <td class="col">
          {{users.getUser(el.assignee).shortName | async}}
        </td>
        <td class="col">{{el.reporter}}
        </td>
        <td class="col">{{el.summary}}</td>
        <td class="col">{{el.description}}</td>
        <td>
          <a [routerLink]='["/issue/edit", el.id]'>
            <input type="submit" value="Edit" class="btn btn-primary">
          </a>
        </td>
      </tr>

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

Расскажите, пожалуйста, как правильно писать в таких ситуациях?
  • Вопрос задан
  • 98 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы