1
1
<!-- - INCLUDE .*/example-([a-z]+)-([0-9a-z]+)\.kt
2
2
/*
3
-
4
3
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
5
4
*/
6
5
@@ -17,6 +16,9 @@ import org.junit.Test
17
16
class SelectGuideTest {
18
17
-->
19
18
19
+
20
+ ## 目录
21
+
20
22
<!-- - TOC -->
21
23
22
24
* [ select 表达式(试验性)] ( #select-expression-experimental )
@@ -30,11 +32,14 @@ class SelectGuideTest {
30
32
31
33
32
34
33
- ## select 表达式(试验性 )
35
+ ## select 表达式(实验性的 )
34
36
35
- select 表达式可以同时等待多个挂起函数,并 _ 选择_ 第一个可用的。
37
+ select 表达式可以同时等待多个挂起函数,并_选择_ <!--
38
+ -->第一个可用的。
36
39
37
- > Select 表达式是 ` kotlinx.coroutines ` 的试验性功能。它们的 API 在 ` kotlinx.coroutines ` 库即将到来的更新中可能会有很大的变化。
40
+ > Select 表达式在 ` kotlinx.coroutines ` 中是一个实验性的特性。这些API在<!--
41
+ --> ` kotlinx.coroutines ` 库即将到来的更新中可能会<!--
42
+ --> 发生改变。
38
43
39
44
### 从通道中查询
40
45
@@ -44,48 +49,50 @@ select 表达式可以同时等待多个挂起函数,并 _选择_ 第一个可
44
49
45
50
``` kotlin
46
51
fun CoroutineScope.fizz () = produce<String > {
47
- while (true ) { // sends "Fizz" every 300 ms
52
+ while (true ) { // 每 300ms 发送 "Fizz"
48
53
delay(300 )
49
54
send(" Fizz" )
50
55
}
51
56
}
52
57
```
53
58
54
-
59
+ </ div >
55
60
56
61
接着 ` buzz ` 每500毫秒产出 “Buzz!” 字符串:
57
62
58
63
<div class =" sample " markdown =" 1 " theme =" idea " data-highlight-only >
59
64
60
65
``` kotlin
61
66
fun CoroutineScope.buzz () = produce<String > {
62
- while (true ) { // sends "Buzz!" every 500 ms
67
+ while (true ) { // 每 500ms 发送 "Buzz!"
63
68
delay(500 )
64
69
send(" Buzz!" )
65
70
}
66
71
}
67
72
```
68
73
74
+ </div >
69
75
70
-
71
- 使用 [ receive] [ ReceiveChannel.receive ] 挂起函数,我们可以从一个或另一个通道接收数据。但是 [ select] 表达式允许我们使用其 [ onReceive] [ ReceiveChannel.onReceive ] 子句同时从两者接收:
76
+ 使用 [ receive] [ ReceiveChannel.receive ] 挂起函数,我们可以从一个或另一个通道接收数据。<!--
77
+ -->但是 [ select] 表达式允许我们使用其<!--
78
+ --> [ onReceive] [ ReceiveChannel.onReceive ] 子句_同时_从两者接收:
72
79
73
80
<div class =" sample " markdown =" 1 " theme =" idea " data-highlight-only >
74
81
75
82
``` kotlin
76
83
suspend fun selectFizzBuzz (fizz : ReceiveChannel <String >, buzz : ReceiveChannel <String >) {
77
- select<Unit > { // <Unit> means that this select expression does not produce any result
78
- fizz.onReceive { value -> // this is the first select clause
84
+ select<Unit > { // <Unit> 意味着该 select 表达式不返回任何结果
85
+ fizz.onReceive { value -> // 这是第一个 select 子句
79
86
println (" fizz -> '$value '" )
80
87
}
81
- buzz.onReceive { value -> // this is the second select clause
88
+ buzz.onReceive { value -> // 这是第二个 select 子句
82
89
println (" buzz -> '$value '" )
83
90
}
84
91
}
85
92
}
86
93
```
87
94
88
-
95
+ </ div >
89
96
90
97
让我们运行7次:
91
98
@@ -99,25 +106,25 @@ import kotlinx.coroutines.channels.*
99
106
import kotlinx.coroutines.selects.*
100
107
101
108
fun CoroutineScope.fizz () = produce<String > {
102
- while (true ) { // sends "Fizz" every 300 ms
109
+ while (true ) { // 每 300ms 发送 "Fizz"
103
110
delay(300 )
104
111
send(" Fizz" )
105
112
}
106
113
}
107
114
108
115
fun CoroutineScope.buzz () = produce<String > {
109
- while (true ) { // sends "Buzz!" every 500 ms
116
+ while (true ) { // 每 500ms 发送 "Buzz!"
110
117
delay(500 )
111
118
send(" Buzz!" )
112
119
}
113
120
}
114
121
115
122
suspend fun selectFizzBuzz (fizz : ReceiveChannel <String >, buzz : ReceiveChannel <String >) {
116
- select<Unit > { // <Unit> means that this select expression does not produce any result
117
- fizz.onReceive { value -> // this is the first select clause
123
+ select<Unit > { // <Unit> 意味着该 select 表达式不返回任何结果
124
+ fizz.onReceive { value -> // 这是第一个 select 子句
118
125
println (" fizz -> '$value '" )
119
126
}
120
- buzz.onReceive { value -> // this is the second select clause
127
+ buzz.onReceive { value -> // 这是第二个 select 子句
121
128
println (" buzz -> '$value '" )
122
129
}
123
130
}
@@ -130,7 +137,7 @@ fun main() = runBlocking<Unit> {
130
137
repeat(7 ) {
131
138
selectFizzBuzz(fizz, buzz)
132
139
}
133
- coroutineContext.cancelChildren() // cancel fizz & buzz coroutines
140
+ coroutineContext.cancelChildren() // 取消 fizz 和 buzz 协程
134
141
// sampleEnd
135
142
}
136
143
```
@@ -155,7 +162,10 @@ buzz -> 'Buzz!'
155
162
156
163
### 从关闭的通道查询
157
164
158
- select 中的 [ onReceive] [ ReceiveChannel.onReceive ] 子句在已经关闭的通道会失败,并导致相应的 ` select ` 抛出异常。我们可以使用 [ onReceiveOrNull] [ ReceiveChannel.onReceiveOrNull ] 子句在关闭通道时执行特定操作。以下示例还显示了 ` select ` 是一个返回其查询方法结果的表达式:
165
+ select 中的 [ onReceive] [ ReceiveChannel.onReceive ] 子句执行在已经关闭的通道会失败,并导致相应的 <!--
166
+ -->` select ` 抛出异常。我们可以使用 [ onReceiveOrNull] [ ReceiveChannel.onReceiveOrNull ] 子句在关闭通道时执行<!--
167
+ -->特定操作。以下示例还显示了 ` select ` 是一个返回<!--
168
+ -->其查询方法结果的表达式:
159
169
160
170
<div class =" sample " markdown =" 1 " theme =" idea " data-highlight-only >
161
171
@@ -179,7 +189,8 @@ suspend fun selectAorB(a: ReceiveChannel<String>, b: ReceiveChannel<String>): St
179
189
180
190
</div >
181
191
182
- 现在有一个产出四次 “Hello” 字符串的 ` a ` 通道、一个产出四次 “World” 字符串的 ` b ` 通道,我们在这两个通道上使用它:
192
+ 现在有一个生成四次 “Hello” 字符串的 ` a ` 通道,<!--
193
+ -->和一个产出四次 “World” 字符串的 ` b ` 通道,我们在这两个通道上使用它:
183
194
184
195
<!-- - CLEAR -->
185
196
@@ -214,7 +225,7 @@ fun main() = runBlocking<Unit> {
214
225
val b = produce<String > {
215
226
repeat(4 ) { send(" World $it " ) }
216
227
}
217
- repeat(8 ) { // print first eight results
228
+ repeat(8 ) { // 打印最早的8个结果
218
229
println (selectAorB(a, b))
219
230
}
220
231
coroutineContext.cancelChildren()
@@ -243,27 +254,31 @@ Channel 'a' is closed
243
254
244
255
有几个结果可以通过观察得出。
245
256
246
- 首先,` select ` 偏向于第一个子句 ,当可以同时选到多个子句时,第一个子句将被选中。在这里,两个通道都在不断地生成字符串,因此作为 select 中的第一个子句的通道获胜。然而因为我们使用的是无缓冲通道,所以 ` a ` 在其发送调用时会不时被挂起,进而 ` b ` 也有机会发送。
247
-
248
- 第二个观察结果是,当通道已经关闭时,会立即选择 [ onReceiveOrNull ] [ ReceiveChannel.onReceiveOrNull ] 。
249
-
257
+ 首先,` select ` _ 偏向于_第一个子句 ,当可以同时选到多个子句时,<!--
258
+ -->第一个子句将被选中。在这里,两个通道都在不断地生成字符串,因此 ` a ` 通道<!--
259
+ -->作为 select 中的第一个子句获胜。然而因为我们使用的是无缓冲通道,所以 ` a ` 在其调用 <!--
260
+ --> [ send ] [ SendChannel.send ] 时会不时地被挂起,进而 ` b ` 也有机会发送。
250
261
262
+ 第二个观察结果是,当通道已经关闭时,<!--
263
+ -->会立即选择 [ onReceiveOrNull] [ ReceiveChannel.onReceiveOrNull ] 。
251
264
252
265
### 查询并发送
253
266
254
- Select 表达式具有 [ onSend] [ SendChannel.onSend ] 子句,可以很好的与选择的偏向特性结合使用。
267
+ Select 表达式具有 [ onSend] [ SendChannel.onSend ] 子句,可以很好的与<!--
268
+ -->选择的偏向特性结合使用。
255
269
256
- 我们来编写一个整数生成器的示例,当主通道上的消费者无法跟上它时,它会将值发送到 ` side ` 通道上:
270
+ 我们来编写一个整数生成器的示例,当主通道上的<!--
271
+ -->消费者无法跟上它时,它会将值发送到 ` side ` 通道上:
257
272
258
273
<div class =" sample " markdown =" 1 " theme =" idea " data-highlight-only >
259
274
260
275
``` kotlin
261
276
fun CoroutineScope.produceNumbers (side : SendChannel <Int >) = produce<Int > {
262
- for (num in 1 .. 10 ) { // produce 10 numbers from 1 to 10
263
- delay(100 ) // every 100 ms
277
+ for (num in 1 .. 10 ) { // 生产从 1 到 10 的10个数值
278
+ delay(100 ) // 每 100 ms
264
279
select<Unit > {
265
- onSend(num) {} // Send to the primary channel
266
- side.onSend(num) {} // or to the side channel
280
+ onSend(num) {} // 发送到主通道
281
+ side.onSend(num) {} // 或者发送到 side 通道
267
282
}
268
283
}
269
284
}
@@ -283,24 +298,24 @@ import kotlinx.coroutines.channels.*
283
298
import kotlinx.coroutines.selects.*
284
299
285
300
fun CoroutineScope.produceNumbers (side : SendChannel <Int >) = produce<Int > {
286
- for (num in 1 .. 10 ) { // produce 10 numbers from 1 to 10
287
- delay(100 ) // every 100 ms
301
+ for (num in 1 .. 10 ) { // 生产从 1 到 10 的10个数值
302
+ delay(100 ) // 每 100 ms
288
303
select<Unit > {
289
- onSend(num) {} // Send to the primary channel
290
- side.onSend(num) {} // or to the side channel
304
+ onSend(num) {} // 发送到主通道
305
+ side.onSend(num) {} // 或者发送到 side 通道
291
306
}
292
307
}
293
308
}
294
309
295
310
fun main () = runBlocking<Unit > {
296
311
// sampleStart
297
- val side = Channel <Int >() // allocate side channel
298
- launch { // this is a very fast consumer for the side channel
312
+ val side = Channel <Int >() // 分配 side channel
313
+ launch { // 对于 side 通道来说,这是一个很快的消费者
299
314
side.consumeEach { println (" Side channel has $it " ) }
300
315
}
301
316
produceNumbers(side).consumeEach {
302
317
println (" Consuming $it " )
303
- delay(250 ) // let us digest the consumed number properly, do not hurry
318
+ delay(250 ) // 让我们不急切地、适当地来消费数值
304
319
}
305
320
println (" Done consuming" )
306
321
coroutineContext.cancelChildren()
@@ -332,7 +347,9 @@ Done consuming
332
347
333
348
### 查询延迟值
334
349
335
- 延迟值可以使用 [ onAwait] [ Deferred.onAwait ] 子句查询。让我们启动一个延迟随机时间后返回延迟字符串的异步方法:
350
+ 延迟值可以使用 [ onAwait] [ Deferred.onAwait ] 子句查询。<!--
351
+ -->让我们启动一个异步函数,它在<!--
352
+ -->随机的延迟后会返回延迟字符串:
336
353
337
354
<div class =" sample " markdown =" 1 " theme =" idea " data-highlight-only >
338
355
@@ -358,7 +375,10 @@ fun CoroutineScope.asyncStringsList(): List<Deferred<String>> {
358
375
359
376
</div >
360
377
361
- 现在主函数等待第一个函数完成,并统计仍处于激活状态的延迟值的数量。注意,我们在这里的使用,事实上是把 ` select ` 表达式作为一种Kotlin DSL,所以我们可以用任意代码为它提供子句。在这种情况下,我们遍历一个延迟值的队列,为每个延迟值提供 ` onAwait ` 子句。
378
+ 现在主函数在等待第一个函数完成,并统计仍处于<!--
379
+ -->激活状态的延迟值的数量。注意,我们在这里的使用,事实上是把 ` select ` 表达式作为一种Kotlin DSL,<!--
380
+ -->所以我们可以用任意代码为它提供子句。在这种情况下,我们遍历一个<!--
381
+ -->延迟值的队列,并为每个延迟值提供 ` onAwait ` 子句。
362
382
363
383
<!-- - CLEAR -->
364
384
@@ -411,26 +431,28 @@ Deferred 4 produced answer 'Waited for 128 ms'
411
431
412
432
### 在延迟值通道上切换
413
433
414
- 我们现在来编写一个通道生产者函数,它消费一个产生延迟字符串的通道,并等待每个接收的延迟值,但只在下一个延迟值到达或者通道关闭之前。此示例将 [ onReceiveOrNull] [ ReceiveChannel.onReceiveOrNull ] 和 [ onAwait] [ Deferred.onAwait ] 子句放在同一个 ` select ` 中:
434
+ 我们现在来编写一个通道生产者函数,它消费一个产生延迟字符串的通道,并等待每个接收的<!--
435
+ -->延迟值,但只在下一个延迟值到达或者通道关闭之前。此示例将 <!--
436
+ -->[ onReceiveOrNull] [ ReceiveChannel.onReceiveOrNull ] 和 [ onAwait] [ Deferred.onAwait ] 子句放在同一个 ` select ` 中:
415
437
416
438
<div class =" sample " markdown =" 1 " theme =" idea " data-highlight-only >
417
439
418
440
``` kotlin
419
441
fun CoroutineScope.switchMapDeferreds (input : ReceiveChannel <Deferred <String >>) = produce<String > {
420
- var current = input.receive() // start with first received deferred value
421
- while (isActive) { // loop while not cancelled/closed
422
- val next = select<Deferred <String >? > { // return next deferred value from this select or null
442
+ var current = input.receive() // 从第一个接收到的延迟值开始
443
+ while (isActive) { // 循环直到被取消或关闭
444
+ val next = select<Deferred <String >? > { // 从这个 select 中返回下一个延迟值或 null
423
445
input.onReceiveOrNull { update ->
424
- update // replaces next value to wait
446
+ update // 替换下一个要等待的值
425
447
}
426
448
current.onAwait { value ->
427
- send(value) // send value that current deferred has produced
428
- input.receiveOrNull() // and use the next deferred from the input channel
449
+ send(value) // 发送当前延迟生成的值
450
+ input.receiveOrNull() // 然后使用从输入通道得到的下一个延迟值
429
451
}
430
452
}
431
453
if (next == null ) {
432
454
println (" Channel was closed" )
433
- break // out of loop
455
+ break // 跳出循环
434
456
} else {
435
457
current = next
436
458
}
@@ -454,7 +476,8 @@ fun CoroutineScope.asyncString(str: String, time: Long) = async {
454
476
455
477
</div >
456
478
457
- 主函数只是启动一个协程来打印 ` switchMapDeferreds ` 的结果并向它发送一些测试数据:
479
+ 主函数只是启动一个协程来打印 ` switchMapDeferreds ` 的结果并向它发送一些<!--
480
+ -->测试数据:
458
481
459
482
<!-- - CLEAR -->
460
483
@@ -466,20 +489,20 @@ import kotlinx.coroutines.channels.*
466
489
import kotlinx.coroutines.selects.*
467
490
468
491
fun CoroutineScope.switchMapDeferreds (input : ReceiveChannel <Deferred <String >>) = produce<String > {
469
- var current = input.receive() // start with first received deferred value
470
- while (isActive) { // loop while not cancelled/closed
471
- val next = select<Deferred <String >? > { // return next deferred value from this select or null
492
+ var current = input.receive() // 从第一个接收到的延迟值开始
493
+ while (isActive) { // 循环直到被取消或关闭
494
+ val next = select<Deferred <String >? > { // 从这个 select 中返回下一个延迟值或 null
472
495
input.onReceiveOrNull { update ->
473
- update // replaces next value to wait
496
+ update // 替换下一个要等待的值
474
497
}
475
498
current.onAwait { value ->
476
- send(value) // send value that current deferred has produced
477
- input.receiveOrNull() // and use the next deferred from the input channel
499
+ send(value) // 发送当前延迟生成的值
500
+ input.receiveOrNull() // 然后使用从输入通道得到的下一个延迟值
478
501
}
479
502
}
480
503
if (next == null ) {
481
504
println (" Channel was closed" )
482
- break // out of loop
505
+ break // 跳出循环
483
506
} else {
484
507
current = next
485
508
}
@@ -494,20 +517,20 @@ fun CoroutineScope.asyncString(str: String, time: Long) = async {
494
517
fun main () = runBlocking<Unit > {
495
518
// sampleStart
496
519
val chan = Channel <Deferred <String >>() // the channel for test
497
- launch { // launch printing coroutine
520
+ launch { // 启动打印协程
498
521
for (s in switchMapDeferreds(chan))
499
- println (s) // print each received string
522
+ println (s) // 打印每个获得的字符串
500
523
}
501
524
chan.send(asyncString(" BEGIN" , 100 ))
502
- delay(200 ) // enough time for "BEGIN" to be produced
525
+ delay(200 ) // 充足的时间来生产 "BEGIN"
503
526
chan.send(asyncString(" Slow" , 500 ))
504
- delay(100 ) // not enough time to produce slow
527
+ delay(100 ) // 不充足的时间来生产 "Slow"
505
528
chan.send(asyncString(" Replace" , 100 ))
506
- delay(500 ) // give it time before the last one
529
+ delay(500 ) // 在最后一个前给它一点时间
507
530
chan.send(asyncString(" END" , 500 ))
508
- delay(1000 ) // give it time to process
509
- chan.close() // close the channel ...
510
- delay(500 ) // and wait some time to let it finish
531
+ delay(1000 ) // 给执行一段时间
532
+ chan.close() // 关闭通道……
533
+ delay(500 ) // 然后等待一段时间来让它结束
511
534
// sampleEnd
512
535
}
513
536
```
@@ -529,7 +552,6 @@ Channel was closed
529
552
530
553
<!-- - MODULE kotlinx-coroutines-core -->
531
554
<!-- - INDEX kotlinx.coroutines -->
532
-
533
555
[ Deferred.onAwait ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/on-await.html
534
556
<!-- - INDEX kotlinx.coroutines.channels -->
535
557
[ ReceiveChannel.receive ] : https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
0 commit comments