Шаблоны для обработки ответа только на последний асинхронный запрос в Angular?

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

У меня есть поисковый ввод, который запрашивает сервер по мере ввода. Я поставил debounce на него, установленный на 300 мс. Однако иногда наблюдается странное поведение:

Пользователь вводит «ab», ждет 300 мс, вводит «c» до того, как первый запрос будет разрешен. В строке поиска они видят "abc", но сетевых запросов теперь два. Иногда второй запрос ("abc") разрешается первым, затем первый запрос ("ab") разрешается и перезаписывает список результатов. Таким образом, пользователь видит список результатов для «ab», но в поисковом запросе есть «abc».

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

Так например - что я хочу

  • типы "ab"
  • отправить запрос "ab"
  • типы "c"
  • отправить запрос "abc"
  • "abc" возвращает ответ, обрабатывает обещание разрешения
  • "ab" возвращает ответ, игнорирует обещание

Существуют ли какие-либо общие шаблоны/подходы для такого рода вещей в Angular? Похоже, это будет общая проблема.

Например, «Разрешить только последнее обещание, которое было создано»?


person AgmLauncher    schedule 22.04.2016    source источник
comment
Вы можете предоставить свой код?   -  person Grundy    schedule 22.04.2016
comment
Действительно сложно решить эту проблему с помощью Promises. Проблема, которую вы описали выше, является идеальным вариантом использования для внедрения RxJS в ваше приложение. С RxJS у вас есть такие операторы, как distinctUntilChanged() и switchMap(), чтобы решить эту проблему.   -  person Dieterg    schedule 22.04.2016
comment
Вы можете создать очередь, см. заголовок stackoverflow.com/questions/26859275/. Хотя шаблон вопроса с тегом jquery также воспроизводится с использованием собственного Promise   -  person guest271314    schedule 22.04.2016
comment
@guest271314 guest271314 это совершенно другой вариант использования...   -  person Dieterg    schedule 22.04.2016
comment
@Dieterg Попробуйте ввести текст в поле ввода в stacksnippets в связанном вопросе. Результаты должны отображаться в последовательном порядке ввода. Предыдущий ввод может быть удален из отображаемых результатов, если появится новый ввод. С точки зрения здесь, не совсем по-другому; хотя может быть неправильной в интерпретации вопроса   -  person guest271314    schedule 22.04.2016
comment
@ guest271314 дело в том, что вполне возможно, что один звонок идет в Индию, а другой - в Америку, в цепочке обещаний просто нет надежного способа узнать, какой звонок вы разрешаете.   -  person Dieterg    schedule 22.04.2016
comment
@Dieerg Должен быть надежный способ определить, какой ввод был помещен в массив очереди, путем ссылки на индекс элемента в массиве. Используйте только последний ввод для отображаемых результатов.   -  person guest271314    schedule 22.04.2016
comment
Давайте продолжим обсуждение в чате.   -  person Dieterg    schedule 22.04.2016


Ответы (2)


Это идеальный вариант использования для введения RxJS, Angular 2 по умолчанию поддерживает RxJS. Однако в Angular 1 также можно использовать эту библиотеку, взгляните на официальную библиотеку rx.angular.js по адресу здесь.

Если вы включите эту библиотеку, вы сможете решить свою проблему следующим образом:

ХМЛ

<input type="text" ng-model="search">

JS

observeOnScope($scope, 'search')
  .debounceTime(300)
  .distinctUntillChanged()
  .switchMap(search)
  .safeApply($scope, function (data) {
     $scope.data = data;
  })
  .subscribe();

function search() {
  return rx.Observable.fromPromise($http({ ... }));
}

Изменить: более подробную статью можно найти по адресу здесь

person Dieterg    schedule 22.04.2016
comment
К сожалению, добавление новых зависимостей в приложение, над которым я работаю, — это процесс, но после того, как вы поиграете с этим, это определенно правильный путь. Пока что я решил отслеживать последний запрос с помощью случайного токена, который я привязываю к обработчику успеха, и если привязанный токен не соответствует текущему токену, то просто выхожу из обработчика. Пока это работает, но я создам технический долг для добавления и рефакторинга RxJS, так как это явно правильный путь. Спасибо за помощь! - person AgmLauncher; 22.04.2016

Вы можете добиться этого, просто используя подписку.

import { Subscription } from 'rxjs';  

export class Component  {
constructor(){}
    subscription: Subscription;

    getData() {
       // just check if subscription is already there then unsubscribe that 
        if (this.subscription) {
          this.subscription.unsubscribe();
        }
        this.subscription = this._http.get(url).subscribe(
          data => {
            console.log(data);
          },
          error => {
            console.log(error);
          }
        )

      }
}
person Dheeraj Kumar    schedule 26.06.2019