【问题标题】:Scala polling loop without vars没有变量的Scala轮询循环
【发布时间】:2021-03-14 22:34:21
【问题描述】:

轮询和 API 的 Scala 最佳实践是什么?

我正在尝试编写一个轮询 API 的 Scala 方法,检查它是否到达"SUCCESS"。在轮询时,它也可能达到像 "FAILED""TIMEOUT 这样的错误状态。

在 Java 中,我会这样写:

public String pollEndpoint() {
  boolean isPolling = true;
  String result = "NA";
  while (isPolling) {
    Response response = getResponse("http://myAPI.com/ready?id=1234");
    if (response.status == "FAILED") { throw new FailedException(response.reason);}
    else ... //Some other bad conditions
    else if (response.status == "SUCCESS") {
      isPolling = false;
      result = response.result; 
    }
    
    System.out.println("Current state is " + response.status); // When running, will be "RUNNING"
    Thread.sleep(1000);
  }
}

在 Scala 中我可以做到:

def pollEndpoint():String = {
  var isPolling = true
  var result = "NA"
  while (isPolling) {
    val response = getResponse("http://myAPI.com/ready?id=1234")
    if (response.status == "FAILED") { throw new FailedException(response.reason)}
    else ... //Some other bad conditions
    else if (response.status == "SUCCESS") {
      isPolling = false
      result = response.result 
    }
    println("Current state is " + response.status); // When running, will be "RUNNING"
    Thread.sleep(1000)  
  }
}

但是这个解决方案使用vars。

有什么好的方法可以做到这一点,只使用vals?

【问题讨论】:

  • 任何while循环都可以重写为一个简单的尾递归函数,基本上你会收到isPolling作为这样一个函数的参数。 - 但是,在这种特定情况下,进行 API 调用 Async 并使用某种 Stream (如 AkkaStreams 或 @ 987654331@) 用于管理轮询率和重试逻辑。

标签: scala polling idioms


【解决方案1】:

正如 Luis 在评论中提到的,您可以为此编写一个递归:

def pollEndpoint():String = {
  val response = getResponse("http://myAPI.com/ready?id=1234")
  println("Current state is " + response.status); // When running, will be "RUNNING"
  if (response.status == "FAILED") { ??? }
  else if (response.status == "SUCCESS") {
    response.result
  } else {
    Thread.sleep(1000)
    pollEndpoint()
  }
}

以下将模拟getResponse

var i = 5
def getResponse(str: String): Response = {
  if (i < 0) {
    Response("SUCCESS", "Great success")
  } else {
    i = i - 1
    Response("Wait", "Not done yet.")
  }
}

然后打电话:

pollEndpoint()

将产生:

Current state is Wait
Current state is Wait
Current state is Wait
Current state is Wait
Current state is Wait
Current state is Wait
Current state is SUCCESS

说了这么多,与其按原样定义getResponse 更好,我们可能会这样做:

def getResponse(str: String): Future[Response]

然后简单地说:

getResponse("http://myAPI.com/ready?id=1234").map { response =>
  // Handle response
}

【讨论】:

  • Future 的问题是现在递归函数有点复杂:) - 这就是为什么最好使用一些可以处理的东西像 Stream 一样容易。
  • @LuisMiguelMejíaSuárez,我不确定 OP 的意思。这看起来像是一个忙碌的等待,只有一个结果,因为成功时他会打破循环并返回值。这就是为什么我认为我们在这里不需要流。感觉更像是一个可以被 future 包装的阻塞 java 代码。
【解决方案2】:

这取决于哪些信息决定了重复(即何时循环)以及将哪些信息返回给外界,但在适当的情况下,您可能会iterate() 而不是循环或递归。

def pollEndpoint():String = {
  Iterator.iterate(getResponse("<yadda-yadda>")){ response =>
    response.status match {
      case "FAILED" => //log error, throw exception, etc.
      case stat     => //log whatever
                       Thread.sleep(1000)
    }
    getResponse("<yadda-yadda>")
  }.dropWhile(_.status != "SUCCESS") //test for exit condition/s
  .next().result
}

【讨论】:

  • 我不熟悉 Iterator.iterate()。为什么需要在 lambda 中调用 getResponse?
  • 在这种情况下,我选择通过调用getResponse() 来结束每次迭代,以便将结果传递给下一次迭代进行评估。这样,原始结果也将针对退出条件进行测试。你可以阅读更多关于.iterate()here的信息。
猜你喜欢
  • 2015-11-20
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 1970-01-01
  • 2012-04-06
  • 2023-03-04
  • 1970-01-01
  • 2018-06-10
相关资源
最近更新 更多