목차
RxJava timer: 지연 뒤 한 번 통지하기
Flowable.timer(1, TimeUnit.SECONDS)는 구독한 뒤 약 1초를 기다려 0L을 한 번 내보내고 완료한다. interval이 주기적으로 번호를 계속 만드는 것과 다르다. 코드를 작성하거나 Flowable 변수를 만든 시각이 아니라 구독 시점부터 지연이 시작된다.

그림의 빈 시간선 뒤 원 하나는 0L, 이어지는 세로선은 정상 완료다. 스케줄러 지연 때문에 정확히 1,000밀리초 뒤에 콜백이 실행된다는 보장은 없다.
한 번 실행하고 완료 기다리기
import io.reactivex.Flowable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class TimerExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch done = new CountDownLatch(1);
Flowable.timer(100, TimeUnit.MILLISECONDS)
.subscribe(
value -> System.out.println("값 " + value),
error -> { error.printStackTrace(); done.countDown(); },
() -> { System.out.println("완료"); done.countDown(); }
);
done.await(2, TimeUnit.SECONDS);
}
}
예상되는 신호 순서는 값 0 → 완료다. 별도 스케줄러를 주지 않으면 RxJava 2의 timer는 Schedulers.computation()을 사용한다. 그래서 구독 호출은 기다리지 않고 돌아올 수 있다. 짧은 main에서는 프로세스가 타이머보다 먼저 종료될 수 있어 위 예제는 CountDownLatch로 관찰 시간을 확보했다. 운영 코드에서는 화면이나 요청의 수명에 맞춰 구독을 관리한다.
지연 신호를 뒤 작업과 연결하기
Flowable.timer(1, TimeUnit.SECONDS)
.concatMap(tick -> Flowable.just("시작"))
.subscribe(System.out::println);
// 구독이 유지되면 약 1초 뒤 '시작', 그 뒤 완료
timer가 0L을 보내면 concatMap이 "시작" 값을 가진 내부 스트림을 구독한다. 내부 스트림이 완료하고 원래 타이머도 완료하므로 전체가 종료된다. 값이 필요 없이 완료 시점만 늦추는 목적이면 Completable.timer가 의도를 더 잘 나타낸다. 지속적인 폴링에는 interval이 맞지만, 그 경우 종료 조건을 따로 만들어야 한다.
취소 핸들을 dispose()하면 예약된 타이머 신호를 더 이상 받지 않는다. 이미 실행을 시작한 다른 작업까지 자동으로 되돌리는 것은 아니므로 타이머 뒤에 외부 I/O를 연결했다면 그 작업의 취소 계약도 확인한다. 지연 작업의 결과를 검증할 때는 실제 시간에 의존하는 sleep 테스트보다 TestScheduler로 가상 시간을 이동하면 빠르고 재현성이 있다.
