简体   繁体   English

在 RxJava 中回滚 Observable?

[英]Rewinding an Observable in RxJava?

I've been applying RxJava and reactive programming to command line applications.我一直在将 RxJava 和反应式编程应用于命令行应用程序。 I've been discovering some pretty awesome design patterns that really simplify the code for a command line interface.我一直在发现一些非常棒的设计模式,它们真正简化了命令行界面的代码。

However, there is one thing I'm struggling with.然而,有一件事我正在挣扎。 Often I am emitting values down a chain of operators, and I concatMap() an Observable.create() to solicit user input on the emitted value.我经常在一系列运算符中发出值,并且我concatMap()Observable.create()来征求用户对发出的值的输入。

This is easy enough, until I want to provide a means to go BACK to the previous emission.这很容易,直到我想提供一种方法来返回到之前的发射。 It starts to get very messy and despite my effort I know this does not work 100% correctly.它开始变得非常混乱,尽管我付出了努力,但我知道这并不能 100% 正确地工作。 I want a user input of "BACK" to try and rewind by one emission by requesting it from a cache.我希望用户输入“BACK”,通过从缓存中请求它来尝试并倒带一次发射。 Is there a better operator or transformer to rewind the emissions at a previous point in the chain and re-send them back up?是否有更好的操作员或变压器来重绕链中前一个点的排放并将它们重新发送回去?

public class Test {

    public static void main(String[] args) {

        final Scanner scanner = new Scanner(System.in);
        final List<String> cache = new ArrayList<>();
        final AtomicInteger cursorIndex = new AtomicInteger(-1);


        Observable.just(
                "What is your name?",
                "What is your quest?",
                "What is your favorite color?",
                "What is the capital of Assyria?",
                "What is the velocity of an unladen swallow?"
        )
        .doOnNext(cache::add)
        .concatMap(q -> {
            int cursor = cursorIndex.get();
            if (cursor >= 0 && cursor < cache.size() - 2) {
                return Observable.just(cache.get(cursor));
            } else {
                cursorIndex.incrementAndGet();
                return Observable.just(q);
            }
        })
        .doOnNext(System.out::println)
        .concatMap(q -> Observable.create(s -> {
                    String answer = scanner.nextLine().trim();

                    if (answer.toUpperCase().equals("BACK")) {
                        cursorIndex.decrementAndGet();
                    }
                    s.onNext(answer);
                    s.onCompleted();
                })
        ).filter(s -> !s.equals("BACK"))
         .subscribe(q -> System.out.println("You said: " + q));

    }
}

    What is your name?
    Sir Lancelot
    You said: Sir Lancelot
    What is your quest?
    To seek the holy grail
    You said: To seek the holy grail
    What is your favorite color?
    BACK
    What is your quest?

What I would do is to have the index as a BehaviorSubject and call onNext on it whenever I have to switch between questions:我要做的是将索引作为 BehaviorSubject 并在我必须在问题之间切换时调用 onNext :

Scanner scanner = new Scanner(System.in);

String[] questions = {
        "What is your name?",
        "What is your quest?",
        "What is your favorite color?",
        "What is the capital of Assyria?",
        "What is the velocity of an unladen swallow?"
};

String[] cache = new String[questions.length];

BehaviorSubject<Integer> index = BehaviorSubject.create(0);

index
.observeOn(Schedulers.trampoline())
.concatMap(idx -> {
    if (idx == questions.length) {
        index.onCompleted();
        return Observable.empty();
    }

    System.out.println(questions[idx]);
    String answer = scanner.nextLine().trim();

    if ("BACK".equals(answer)) {
        index.onNext(Math.max(0, idx - 1));
        return Observable.empty();
    } else
    if ("QUIT".equals(answer)) {
        index.onCompleted();
        return Observable.empty();
    }

    cache[idx] = answer;
    index.onNext(idx + 1);
    return Observable.just(answer);
})
.subscribe(v -> System.out.println("You said: " + v));

I suppose there is one way if the Observable is finite and has a manageable number of emissions (which should be the case if each one requires a user input).我想如果Observable是有限的并且具有可管理的排放数量(如果每个都需要用户输入应该是这种情况),我想有一种方法。 You could collect it into a toList() and create a new Observable off that list.您可以将其收集到toList()并从该列表中创建一个新的Observable You can then control the iteration and use a boolean flag to signal a rewind at any moment.然后,您可以控制迭代并使用布尔标志随时发出倒带信号。

public class Test {

    public static void main(String[] args) {

        final Scanner scanner = new Scanner(System.in);
        final AtomicBoolean rewind = new AtomicBoolean();

        Observable.just(
                "What is your name?",
                "What is your quest?",
                "What is your favorite color?",
                "What is the capital of Assyria?",
                "What is the velocity of an unladen swallow?"
        ).toList().concatMap(l -> Observable.create(s -> {
            for (int i = 0; i < l.size(); i++) {
                s.onNext(l.get(i));
                if (rewind.getAndSet(false) &&  i >= 1) {
                    i = i - 2;
                }
            }
            s.onCompleted();
        }))
        .doOnNext(System.out::println)
        .concatMap(q -> Observable.create(s -> {
                    String answer = scanner.nextLine().trim();

                    if (answer.toUpperCase().equals("BACK")) {
                        rewind.set(true);
                    }
                    s.onNext(answer);
                    s.onCompleted();
                })
        ).filter(s -> !s.equals("BACK")).subscribe(q -> System.out.println("You said: " + q));

    }
}

What is your name?
Sir Lancelot of Camelot
You said: Sir Lancelot of Camelot
What is your quest?
To seek the Holy Grail
You said: To seek the Holy Grail
What is your favorite color?
Blue
You said: Blue
What is the capital of Assyria?
Asher
You said: Ashur
What is the velocity of an unladen swallow?
BACK
What is the capital of Assyria?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM