Java 8 - 重试方法,直到满足条件(间隔)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30989558/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 17:55:53  来源:igfitidea点击:

Java 8 - retry a method until a condition is fulfilled (in intervals)

javajava-8hamcrest

提问by Bick

I want to create a class that can run a method until a condition about the return value is fulfilled.

我想创建一个可以运行方法的类,直到满足有关返回值的条件。

It should look something like this

它应该看起来像这样

methodPoller.poll(pollDurationSec, pollIntervalMillis)
            .method(dog.bark())
            .until(dog -> dog.bark().equals("Woof"))
            .execute();

My method poller look somewhat like this () // following GuiSim answer

我的方询器看起来有点像这样 () // 按照 GuiSim 的回答

public class MethodPoller {
    Duration pollDurationSec;
    int pollIntervalMillis;


    public MethodPoller() {
    }

    public MethodPoller poll(Duration pollDurationSec, int pollIntervalMillis) {
        this.pollDurationSec = pollDurationSec;
        this.pollIntervalMillis = pollIntervalMillis;
        return this;
    }

    public <T> MethodPoller method(Supplier<T> supplier) {

        return this;
    }

    public <T> MethodPoller until(Predicate<T> predicate) {

        return this;
    }
}

But I am having a hard time going opn from here.
How can I implement a retry to a general method until a condition is fulfilled?
Thanks.

但我很难从这里开始。
在满足条件之前,如何实现对通用方法的重试?
谢谢。

回答by GuiSim

Yes, this can easily be done in Java 7 and even cleaner using Java 8.

是的,这可以在 Java 7 中轻松完成,使用 Java 8 甚至更干净。

The parameter to your methodmethod should be a java.util.function.Supplier<T>and the parameter to your untilmethod should be a java.util.function.Predicate<T>.

你的method方法的参数应该是 a java.util.function.Supplier<T>,你的until方法的参数应该是 a java.util.function.Predicate<T>

You can then use method references or lambda expressions to create you Poller like so:

然后,您可以使用方法引用或 lambda 表达式来创建您的 Poller,如下所示:

myMethodPoller.poll(pollDurationInteger, intervalInMillisecond)
          .method(payment::getStatus)
          .until (paymentStatus -> paymentStatus.getValue().equals("COMPLETED"))
          .execute();

As a side note, if you're going to use Java 8, I'd recommend using java.time.Durationinstead of an integer to represent the poll duration and the interval.

附带说明一下,如果您打算使用 Java 8,我建议您使用java.time.Duration而不是整数来表示轮询持续时间和间隔。

I'd also recommend looking into https://github.com/rholder/guava-retryingwhich is a library that you could perhaps use. If not, it could be a good inspiration for your API as it features a nice fluent API.

我还建议您查看https://github.com/rholder/guava-retrying,这是一个您可能会使用的库。如果没有,它可能是您 API 的一个很好的灵感来源,因为它具有很好的流畅 API。

EDIT: Following the update to the question, here is a simple implementation. I've left some parts for you to complete as TODOs.

编辑:在更新问题之后,这是一个简单的实现。我已经留下了一些部分供您作为 TODO 完成。

import java.time.Duration;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class MethodPoller<T> {

    Duration pollDurationSec;
    int pollIntervalMillis;

    private Supplier<T> pollMethod = null;
    private Predicate<T> pollResultPredicate = null;

    public MethodPoller() {
    }

    public MethodPoller<T> poll(Duration pollDurationSec, int pollIntervalMillis) {
        this.pollDurationSec = pollDurationSec;
        this.pollIntervalMillis = pollIntervalMillis;
        return this;
    }

    public MethodPoller<T> method(Supplier<T> supplier) {
        pollMethod = supplier;
        return this;
    }

    public MethodPoller<T> until(Predicate<T> predicate) {
        pollResultPredicate = predicate;
        return this;
    }

    public T execute()
    {
        // TODO: Validate that poll, method and until have been called.

        T result = null;
        boolean pollSucceeded = false;
        // TODO: Add check on poll duration
        // TODO: Use poll interval
        while (!pollSucceeded) {
            result = pollMethod.get();
            pollSucceeded = pollResultPredicate.test(result);
        }

        return result;
    }
}

Sample use:

样品用途:

import static org.junit.Assert.assertTrue;
import java.util.UUID;
import org.junit.Test;

public class MethodPollerTest
{

    @Test
    public void test()
    {
        MethodPoller<String> poller = new MethodPoller<>();
        String uuidThatStartsWithOneTwoThree = poller.method(() -> UUID.randomUUID().toString())
                                                     .until(s -> s.startsWith("123"))
                                                     .execute();
        assertTrue(uuidThatStartsWithOneTwoThree.startsWith("123"));
        System.out.println(uuidThatStartsWithOneTwoThree);
    }
}

回答by Kkkev

Instead of writing this yourself, could you use Awaitility?

你可以使用Awaitility代替自己写这个吗?

await()
    .atMost(3, SECONDS)
    .until(dog::bark, equalTo("woof"));

回答by Jonathan

Here's a solution using Failsafe:

这是使用Failsafe的解决方案:

RetryPolicy retryPolicy = new RetryPolicy()
  .retryIf(bark -> bark.equals("Woof"))
  .withMaxDuration(pollDurationSec, TimeUnit.SECONDS);
  .withDelay(pollIntervalMillis, TimeUnit.MILLISECONDS);

Failsafe.with(retryPolicy).get(() -> dog.bark());

Pretty straightforward as you can see, and handles your exact scenario.

如您所见,非常简单,并处理您的确切场景。

回答by frhack

You can use RxJava

你可以使用RxJava

  Observable.interval(3, TimeUnit.SECONDS, Schedulers.io())
                .map(tick -> dog)
                .takeWhile( dog-> { return ! dog.bark().equals("Woof"); })
                .subscribe(dog ->dog.bark());


        try {
            Thread.sleep(10000);
        }catch(Exception e){}

http://blog.freeside.co/2015/01/29/simple-background-polling-with-rxjava/

http://blog.freeside.co/2015/01/29/simple-background-polling-with-rxjava/