#angular #rxjs
#angular #rxjs
Вопрос:
У меня есть функция в angular, которая возвращает набор данных со статусом «Продолжить» или «завершено». Я хочу продолжать нажимать на вызов до тех пор, пока статус не вернется «завершено», но максимум на одну минуту. Как я могу добиться этого в angular
public getLeadsResponse(key: any) {
this._service.getLeadsData(key).subscribe((response: any) => {
if (response) {
if (response.payload.status == "running") {
// here if i repeate this function it will start an infinite loop if status is always "running"
this.getLeadsResponse(key);
}
else if (response.payload.status == "finished") {
this.items = res.payload.resu<
}
}
})
}
Ответ №1:
Вы можете сделать это с помощью expand()
:
import { of, EMPTY, timer } from 'rxjs';
import { expand, map, filter, takeUntil } from 'rxjs/operators';
const makeHttpRequest$ = timer(1000).pipe(map(() => Math.random() > 0.9 ? 'finished' : 'continue'));
const source = of(null).pipe(
expand(value => value === 'finished' ? EMPTY : makeHttpRequest$),
filter(value => value !== null), // Ignore the first `of(null)` that triggered the chain.
takeUntil(timer(60 * 1000)),
);
source.subscribe(console.log);
Живая демонстрация: https://stackblitz.com/edit/rxjs-8n11jj?devtoolsheight=60
Комментарии:
1. куда я должен поместить свой _service call @martin? в math.random() месте..
2. Извините, если это глупый вопрос, я новичок в rxjs
3. Вместо
makeHttpRequest$
4. Вы должны вернуть наблюдаемое
expand
значение, чтобы оно было там, где вы его разместили5. @ martin в троичном операторе вы используете EMPY, что я должен поместить туда вместо ПУСТОГО?
Ответ №2:
В дополнение к ответу @martin вы также можете выполнить явную проверку метки времени в течение 1 минуты с помощью timestamp
оператора RxJS.
Я использовал RxJS timer
для отправки каждых ‘n’ временных единиц и переключения на HTTP-запрос для каждого из них с использованием switchMap
оператора.
Попробуйте следующее
import { timer } from "rxjs";
import { filter, map, timestamp, switchMap } from "rxjs/operators";
public getLeadsResponse(key: any) {
const now = Date.now();
const subscription = timer(0, 6000).pipe( // <-- adjust polling frequency
switchMap(_ => this._service.getLeadsData(key)),
timestamp(),
map((data: { timestamp: number; value: any }) => {
if (data.timestamp - now < 60000) {
return data.value;
} else {
subscription.unsubscribe(); // <-- close subscription if 1 min has elapsed
}
}),
filter(response => response.payload.status == "finished")
).subscribe({
next: response => {
this.items = res.payload.result;
subscription.unsubscribe(); // <-- close subscription if status is "finished"
},
error: error => {
// handle error
}
});
}
Ответ №3:
Я думаю, вы можете добавить только тайм-аут к вашему ajax-запросу, который «разорвет» соединение с сервером, если это произойдет. Таким образом, вы не сможете проверить значение результата полезной нагрузки.
Вы можете реализовать желаемое поведение другими способами :
- сделайте свой серверный процесс асинхронным, чтобы вы могли выполнять ajax-запрос, скажем, каждые 5 секунд, чтобы проверить текущее состояние, и маршрут, по которому вы добираетесь, должен быстро реагировать и просто «проверять статус операции». тогда логика проверки не более 1 минуты будет в вашем интерфейсном приложении
- откройте соединение с длинным опросом, которое может отправлять несколько сообщений, используя, например, SSE
- Сделайте то же самое, что и выше, используя websocket.
Во всех случаях реальный дизайн заключается в том, что работа вашего внутреннего сервера должна быть асинхронной, и он может передавать свой текущий статус другому процессу / потоку.
Обратите внимание, что в какой-то момент при разработке API вы заканчиваете тем, что делаете подобные вещи. Добавить некоторую асинхронную обработку в проект не так просто, как выполнять простые длинные запросы и усложнять проект, но у этого есть некоторые преимущества, такие как отсутствие перегрузки серверов и более быстрое реагирование на пользовательские интерфейсы.
Комментарии:
1. правильно. но проблема в том, что у нас нет доступа к серверной части. нам просто нужно иметь дело с API. пожалуйста, любое решение с примером.
2. Мое предложение в основном касается внутренних изменений, которые в вашем случае кажутся невозможными. В случае, если в вашем бэкэнде возможны изменения, вы можете добавить маршрут для «регистрации» задачи для обработки в базе данных, например. затем другой процесс на серверной части сервера часто ищет задачи для продолжения, если таковые имеются, запускает фоновый длительный процесс и обновляет статус задачи в базе данных, чтобы другой маршрут с параметром идентификатора задачи мог возвращать статус задачи в базу данных. Есть много способов реализовать это из scrach, используя системы задач, такие как celery docs.celeryproject.org или даже более сложные системы очередей.