scala不可变和可变

by Martin Budi

马丁·布迪(Martin Budi)

Scala使期货变得轻松 (Futures Made Easy with Scala)

Future is an abstraction to represent the completion of an asynchronous operation. Today it is commonly used in popular languages from Java to Dart. However, as modern applications are becoming more complex, composing them is also becoming more difficult. Scala utilizes a functional approach that makes it easy to visualize and construct Future composition.

Future是代表异步操作完成的抽象。 如今,它已广泛用于从Java到Dart的流行语言。 但是,随着现代应用程序变得越来越复杂,编写它们的难度也越来越大。 Scala利用一种功能性方法,可以轻松地可视化和构造Future构图。

This article aims to explain the basics in a pragmatic way. No jargon, no foreign terminology. You don’t even have to be a Scala programmer (yet). All you need to have is some understanding of a couple of higher-order functions: map and foreach. So let’s get started.

本文旨在以务实的方式解释基础知识。 没有行话,没有外来术语。 您甚至不必成为Scala程序员(尚未)。 您需要做的就是对一些高阶函数有一些了解:map和foreach。 因此,让我们开始吧。

In Scala, a future can be created as simple as this:

在Scala中,可以这样创建一个Future:

Future {"Hi"}

Now let’s run it and make a “Hi World”.

现在运行它并创建一个“ Hi World”。

Future {"Hi"} .foreach (z => println(z + " World"))

That’s all there is. We just ran a future using foreach, manipulated the result a bit, and printed it to the console.

这就是全部。 我们只是使用foreach运行了一个未来,对结果进行了一些处理,然后将其打印到控制台。

But how is it possible? So we normally associate foreach and map with collections: we unwrap the content and tinker with it. If you look at it, it’s conceptually similar to a future in the way we want to unwrap the output from Future{}and manipulate it. To have this happen the future needs to be completed first, hence “running” it. This is the reasoning behind the functional composition of Scala Future.

但是怎么可能呢? 因此,我们通常将foreach关联起来并与集合进行映射:我们将内容解开包装并进行修改。 如果您查看一下,它在概念上就类似于将来,我们希望从Future{}解开输出并对其进行操作。 为了使这种情况发生,未来需要先完成,然后“运行”。 这是Scala Future功能组成背后的原因。

In realistic applications, we want to coordinate not just one but several futures at once. A particular challenge is how to arrange them to run sequentially or simultaneously.

在实际应用中,我们不仅要协调一个未来,还要协调多个未来。 一个特殊的挑战是如何安排它们顺序同时运行。

顺序运行 (Sequential run)

When several futures start one after another like a relay race we call it sequential run. A typical solution would simply be placing a task in the previous task’s callback, a technique known as chaining. The concept is correct but it doesn’t look pretty.

当几个期货像接力赛一样接连开始时,我们称之为连续运行。 一个典型的解决方案就是将一个任务放在上一个任务的回调中,这是一种称为链接的技术。 这个概念是正确的,但看起来并不漂亮。

In Scala, we can use for-comprehension to help us abstract it. To see how it looks, let’s just go straight to an example.

在Scala中,我们可以使用理解来帮助我们对其进行抽象。 要查看它的外观,我们直接看一个例子。

import scala.concurrent.ExecutionContext.Implicits.globalobject Main extends App {def job(n: Int) = Future {Thread.sleep(1000)println(n) // for demo only as this is side-effecting n + 1}val f = for {f1 <- job(1)f2 <- job(f1)f3 <- job(f2)f4 <- job(f3)f5 <- job(f4)} yield List(f1, f2, f3, f4, f5)f.map(z => println(s"Done. ${z.size} jobs run"))Thread.sleep(6000) // needed to prevent main thread from quitting // too early
}

The first thing to do is importing ExecutionContext whose role is to manage thread pool. Without it, our future will not run.

首先要做的是导入ExecutionContext,其作用是管理线程池。 没有它,我们的未来将无法运转。

Next, we define our “big job” which simply waits for a second and returns its input incremented by one.

接下来,我们定义“大工作”,仅等待一秒钟,然后将其输入增加一。

Then we have our for-comprehension block. In this structure, each line inside assigns a job’s result to a value with &lt;- which will then be available for any subsequent futures. We have arranged our jobs so that except for the first one, each one takes in the output of the previous job.

然后,我们获得了理解力。 在这种结构中,内部的每一行都将作业的结果分配为&l t;-的值,该值随后可用于任何后续期货。 我们已经安排好工作,以便除第一个工作外,每个工作都接上一个工作的输出。

Also, note that the result of a for-comprehension is also a future with output determined by yield. After the execution, the result will be available inside map. For our purpose, we simply put all the jobs’ outputs in a list and take its size.

另外,请注意,理解的结果也是未来,产量取决于产量。 执行后,结果将在map内可用。 出于我们的目的,我们仅将所有作业的输出放在列表中并确定其大小。

Let’s run it.

让我们运行它。

We can see the five futures fired one-by-one. It is important to note that this arrangement should only be used when the future is dependent on the previous future.

我们可以看到五种期货一一交易。 重要的是要注意,仅当将来取决于先前的将来时,才应使用此安排。

同时或并行运行 (Simultaneous or Parallel run)

If the futures are independent of each other then they should be fired simultaneously. For this purpose, we’re going to use Future.sequence. The name is a bit confusing, but in principle it simply takes a list of futures and transforms it into a future of list. The evaluation, however, is done asynchronously.

如果期货彼此独立,则应同时射击。 为此,我们将使用Future.sequence 。 名称有点混乱,但是原则上它只是获取期货列表并将其转换为列表的期货。 但是,评估是异步进行的。

Let’s create an example of mixed sequential and parallel futures.

让我们创建一个混合有序和并行期货的例子。

val f = for {f1 <- job(1)f2 <- Future.sequence(List(job(f1), job(f1)))f3 <- job(f2.head)f4 <- Future.sequence(List(job(f3), job(f3)))f5 <- job(f4.head)
} yield f2.size + f4.size
f.foreach(z => println(s"Done. $z jobs run in parallel"))

Future.sequence takes a list of futures that we wish to run simultaneously. So here we have f2 and f4 containing two parallel jobs. As the argument fed into Future.sequence is a list, the result is also a list. In a realistic application, the results may be combined for further computation. Here we’ll take the first element from each list with .head then pass it to f3 and f5 respectively.

Future.sequence列出了我们希望同时运行的期货列表。 所以在这里,我们有f2和f4包含两个并行作业。 由于传入Future.sequence的参数是一个列表,所以结果也是一个列表。 在实际应用中,可以将结果组合起来以进行进一步的计算。 在这里,我们将使用.head从每个列表中获取第一个元素,然后分别将其传递给f3和f5。

Let’s see it in action:

让我们来看看它的作用:

We can see the jobs in 2 and 4 fired simultaneously indicating successful parallelism. It is worth noting that parallel execution is not always guaranteed since it depends on available threads. If there are not enough threads then only some of the jobs will run in parallel. The others, however, will wait until some more threads are freed.

我们可以看到同时触发了2和4的作业,这表明并行处理成功。 值得注意的是,并行执行并非总是保证的,因为它取决于可用线程。 如果没有足够的线程,则只有部分作业将并行运行。 但是,其他线程将等待,直到释放更多线程。

从错误中恢复 (Recovering from errors)

Scala Future incorporates recover that acts as a back-up future when an error occurs. This allows the future composition to finish even with failures. To illustrate, consider this code:

Scala Future合并了恢复 ,当发生错误时,该恢复充当备份的未来 这使得将来的合成即使失败也可以完成。 为了说明,请考虑以下代码:

Future {"abc".toInt}
.map(z => z + 1)

Of course, this will not work, as “abc” is not an int. With recover, we can salvage it by passing a default value. Let’s try passing a zero:

当然,这将不起作用,因为“ abc”不是整数。 使用recover,我们可以通过传递默认值来挽救它。 让我们尝试传递零:

Future {"abc".toInt}
.recover {case e => 0}
.map(z => z + 1)

Now the code will run and produce one as a result. In composition, we can fine-tune each future like this to make sure the process won’t fail.

现在,代码将运行并生成一个结果。 在合成方面,我们可以像这样微调每个未来,以确保过程不会失败。

However, there are also times when we want to reject errors explicitly. For this purpose, we can use Future.succesful and Future.failed to signal validation result. And if we don’t care about individual failure we can position recover to catch any error inside the composition.

但是,有时我们也希望明确拒绝错误。 为此,我们可以使用Future.succesful和Future.failed来发送验证结果。 而且,如果我们不关心单个故障,则可以定位恢复以捕获组合中的任何错误。

Let’s work another bit of code using for-comprehension that checks if the input is a valid int and lower than 100. Future.failed and Future.successful are both futures so we don’t need to wrap it in one. Future.failed in particular requires a Throwable so we’re going to create a custom one for input larger than 100. After putting it all up together we would have as follows:

让我们使用for-comprehension编写另一段代码,该代码检查输入是否为有效的int且小于100。Future.failed和Future.successful均为Future,因此我们无需将其包装在一起。 特别是Future.failed需要Throwable,因此我们将为输入大于100的对象创建一个自定义对象。将所有内容放在一起后,我们将具有以下内容:

val input = "5" // let's try "5", "200", and "abc"
case class NumberTooLarge() extends Throwable()
val f = for {f1 <- Future{ input.toInt }f2 <- if (f1 > 100) {Future.failed(NumberTooLarge())} else {Future.successful(f1)}
} yield f2
f map(println) recover {case e => e.printStackTrace()}

Notice the positioning of recover. With this configuration, it will simply intercept any error occurring inside the block. Let’s test it with several different inputs “5”, “200”, and “abc”:

注意恢复的位置。 使用此配置,它将仅拦截块内发生的任何错误。 让我们用几个不同的输入“ 5”,“ 200”和“ abc”对其进行测试:

"5"   -> 5
"200" -> NumberTooLarge stacktrace
"abc" -> NumberFormatException stacktrace

“5” reached the end no problem. “200” and “abc” arrived in recover. Now, what if we want to handle each error separately? This is where pattern matching comes into play. Expanding the recover block, we can have something like this:

“ 5”到达终点没问题。 “ 200”和“ abc”到达恢复状态。 现在,如果我们要分别处理每个错误怎么办? 这就是模式匹配起作用的地方。 扩展recover块,我们可以得到以下内容:

case e => e match {case t: NumberTooLarge => // deal with number > 100case t: NumberFormatException => // deal with not a numbercase _ => // deal with any other errors}
}

You might probably have guessed it but an all-or-nothing scenario like this is commonly used in public APIs. Such service wouldn’t process invalid input but needs to return a message to inform the client what they did wrong. By separating exceptions, we can pass a custom message for each error. If you like to build such service (with a very fast web framework), head over to my Vert.x article.

您可能已经猜到了,但是在公共API中通常使用这种全有或全无的方案。 此类服务不会处理无效的输入,但需要返回一条消息以告知客户端他们做错了什么。 通过分离异常,我们可以为每个错误传递自定义消息。 如果您想构建这样的服务(使用非常快速的Web框架),请转至我的Vert.x文章 。

Scala以外的世界 (The world outside Scala)

We have talked a lot about how easy Scala Future is. But is it really? To answer it we need to look at how it’s done in other languages. Arguably the closest language to Scala is Java as both operate on JVM. Furthermore, Java 8 has introduced Concurrency API with CompletableFuture which is also able to chain futures. Let’s rework the first sequence example with it.

我们已经谈论了很多有关Scala Future的事情。 但这是真的吗? 要回答这个问题,我们需要看看它是如何用其他语言完成的。 可以说,最接近Scala的语言是Java,因为两者都在JVM上运行。 此外,Java 8引入了带有CompletableFuture的并发API,该API也能够链接期货。 让我们用它重做第一个序列示例。

That’s sure a lot of stuff. And to code this I had to look up supplyAsync and thenApply among so many methods in the documentation. And even if I know all these methods, they can only be used within the context of the API.

那肯定有很多东西。 为了对此进行编码,我必须在文档中的众多方法中查找supplyAsync,然后应用 。 即使我知道所有这些方法,也只能在API上下文中使用它们。

On the other hand, Scala Future is not based on API or external libraries but a functional programming concept that is also used in other aspects of Scala. So with an initial investment in covering the fundamentals, you can reap the reward of less overhead and higher flexibility.

另一方面,Scala Future不是基于API或外部库,而是一个功能性编程概念,该概念也已在Scala的其他方面使用。 因此,通过对基础知识进行初步投资,您可以获得较低的开销和更高的灵活性的回报。

结语 (Wrapping up)

That’s all for the basics. There’s more to Scala Future but what we have here has covered enough ground to build real-life applications. If you like to read more about Future or Scala, in general, I’d recommend Alvin Alexander tutorials, AllAboutScala, and Sujit Kamthe’s article that offers easy to grasp explanations.

这就是基本知识。 Scala Future还有更多功能,但是我们这里已经涵盖了足够的基础来构建实际应用程序。 如果您想阅读有关Future或Scala的更多信息,通常,我建议Alvin Alexander教程 , AllAboutScala和Sujit Kamthe的文章 ,这些文章提供了易于理解的解释。

翻译自: https://www.freecodecamp.org/news/futures-made-easy-with-scala-da1beb3bb281/

scala不可变和可变

scala不可变和可变_Scala使期货变得轻松相关推荐

  1. Interview:算法岗位面试—10.11下午—上海某公司算法岗位(偏机器学习,互联网数字行业)技术面试考点之XGBoost的特点、python的可变不可变的数据类型、赋值浅拷贝深拷贝区别

    ML岗位面试:10.11下午-上海某公司算法岗位(偏机器学习,互联网数字行业)技术面试考点之XGBoost的特点.python的可变不可变的数据类型.赋值浅拷贝深拷贝区别 Interview:算法岗位 ...

  2. 自动生成sqlserver增删改成_如何批量生成证书证件-可变条码-可变图片-可变数据-快速教程...

    如何生成可变图像? 如何生成可变文本? 如何生成可变条码? 如何使打印作业自动化? 从多页设计中选取页 使用几乎所有数据库程序中的数据库信息 通过"拖放"创建个性化打印 分享快速教 ...

  3. Java的知识点21——String类、StringBuffer和StringBuilder、不可变和可变字符序列使用陷阱

    String类 String 类对象代表不可变的Unicode字符序列,因此我们可以将String对象称为"不可变对象" substring()是对字符串的截取操作,但本质是读取原 ...

  4. 2017.7.18可变/不可变类型,符号运算及其流程控制

    1.可变/不可变类型 可变数据类型:指的是数据id不变的情况下,数值可变 例子:列表   字典 不可变数据类型:是数据本身的id.value都不可改变的数据 例子:数字   字符串   布尔值 2.运 ...

  5. React Native之(var和let区别 )(简单解构)(map对象遍历)(可变顺序参数和不可以变顺序参数函数)

    1 var和let区别 let左右范围在块里面,var定义的变量可提升,用let声明的变量不可以声明2次 2 简单解构 let [a, b, c] = [1, 2, 3]; 3  map对象遍历 co ...

  6. 【软件构造】数据类型、类型检查、可变不可变数据类型

    文章目录 一.基本数据类型 和 对象数据类型 1.基本数据类型: 2.对象数据类型: 二.静态类型检查 和 动态类型检查 1.静态类型检查 2.动态类型检查 三.Mutable可变对象 和 Immut ...

  7. python的内置数据结构可变不可变_如何在Python中实现不可变的数据结构?

    问题 您需要在Python中实现不可变的数据结构. 介绍.. 当您要防止多个人同时在并行编程中修改一条数据时,不可变数据结构非常方便.可变数据结构(例如Array)可以随时更改,而可变数据结构则不能更 ...

  8. 使网页变灰的代码(包括FLASH等所有网页元素).

    使网页所有元素变灰的代码:把下面的代码加入网站的CSS文件中 第一种方法: html{ filter:progid:DXImageTransform.Microsoft.BasicImage(gray ...

  9. CSS 使网页变灰(文字加动画)

    如果需要使网站变灰,在网页上加层滤镜就可以了. 1 html { filter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1); } ...

最新文章

  1. nagios总结与基本配置模板-V2
  2. python3爬虫小型代码_python3简单爬虫实现代码
  3. linux:系统对open files的限制
  4. Android10不能用谷歌,谷歌真的很严格,一大波老APP将不能在安卓10.0运行
  5. 程序员的灯下黑:重知识轻技术(转)
  6. as3 htmlText 的bug
  7. java开发项目经验_Java项目经验——程序员成长的钥匙
  8. 风变Python编程13类的学习2
  9. centos7 mysql添加密码_centos-在Centos7上更改mysql根密码
  10. linux定时任务的配置详解
  11. 分享几十年来记录下的编程技巧
  12. js模拟鼠标自动滑动滑块--dispatchEvent
  13. Ubuntu 18.04及几款应用的安装
  14. PostgreSQL登录及修改密码
  15. 达内python怎么样_在达内学Python怎么样?我能学会吗?
  16. 北京市道路街道区县shape分享
  17. 苹果六电池_苹果官网上架新品,18999元起~
  18. Swarm简介和使用教程-Docker-swarm和Docker-machine
  19. SCAU2021春季个人排位赛第七场 (部分题解))
  20. shell181网格划分_ANSYS软件中SHELL181单元参数详解(1)

热门文章

  1. javascript 西瓜一期 03 机器语言与高级语言
  2. bootstrap-表单
  3. linux-文件类型与查看文件型
  4. composer 中国镜像
  5. 客户端远程连接Oracle数据库
  6. js 执行环境 活动对象 变量对象 作用域链的理解
  7. SEO之网站内链优化策略
  8. 用Canvas为网页加入动态背景
  9. 面向对象要点(构造函数)
  10. Jeecg-boot 使用心得建议