8000 Fix trasient typo; make general syntax and punctuation edits to readm… · socketry/async@5423657 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5423657

Browse files
Fix trasient typo; make general syntax and punctuation edits to readme (#235)
Co-authored-by: Olle Jonsson <olle.jonsson@gmail.com>
1 parent 71cb14e commit 5423657

File tree

1 file changed

+34
-30
lines changed

1 file changed

+34
-30
lines changed

guides/asynchronous-tasks/readme.md

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This guide explains how asynchronous tasks work and how to use them.
44

55
## Overview
66

7-
Tasks are the smallest unit of sequential code execution in {ruby Async}. Tasks can create other tasks, and Async tracks the parent-child relationship between tasks. When stopping a parent task, it will also stop all it's children tasks. The event loop generally has one root task.
7+
Tasks are the smallest unit of sequential code execution in {ruby Async}. Tasks can create other tasks, and Async tracks the parent-child relationship between tasks. When a parent task is stopped, it will also stop all its children tasks. The outer event loop generally has one root task.
88

99
```mermaid
1010
graph LR
@@ -39,15 +39,15 @@ stateDiagram-v2
3939
stopped --> [*]
4040
```
4141

42-
Tasks are created in the initialized state, and are run by the event loop. During the execution, a task can either complete successfully, fail with an unhandled exception, or be explicitly stopped. In all of these cases, you can wait for a task to complete by using {ruby Async::Task#wait}.
42+
Tasks are created in the `initialized` state, and are run by the event loop. During the execution, a task can either `complete` successfully, become `failed` with an unhandled exception, or be explicitly `stopped`. In all of these cases, you can wait for a task to complete by using {ruby Async::Task#wait}.
4343

4444
1. In the case the task successfully completed, the result will be whatever value was generated by the last expression in the task.
4545
2. In the case the task failed with an unhandled exception, waiting on the task will re-raise the exception.
4646
3. In the case the task was stopped, the result will be `nil`.
4747

4848
## Starting A Task
4949

50-
At any point in your program, you can start a task using the {ruby Kernel::Async} method:
50+
At any point in your program, you can start the reactor and a root task using the {ruby Kernel::Async} method:
5151

5252
```ruby
5353
Async do
@@ -100,7 +100,7 @@ end
100100

101101
### Performance Considerations
102102

103-
Task creation and execution has been heavily optimised. Do not trade program complexity to avoid creating tasks, the cost will almost never be useful.
103+
Task creation and execution has been heavily optimised. Do not trade program complexity to avoid creating tasks; the cost will almost always exceed the gain.
104104

105105
Do consider using correct concurrency primatives like {ruby Async::Semaphore}, {ruby Async::Barrier}, etc, to ensure your program is well-behaved in the presence of large inputs (i.e. don't create an unbounded number of tasks).
106106

@@ -124,7 +124,7 @@ end
124124

125125
## Waiting for Tasks
126126

127-
Waiting for a single task is trivial, simply invoke {ruby Async::Task#wait}. To wait for multiple tasks, you may want to use a {ruby Async::Barrier}. You can use {ruby Async::Barrier#async} to create multiple child tasks, and wait for them all to complete using {ruby Async::Barrier#wait}.
127+
Waiting for a single task is trivial: simply invoke {ruby Async::Task#wait}. To wait for multiple tasks, you may either {ruby Async::Task#wait} on each in turn, or you may want to use a {ruby Async::Barrier}. You can use {ruby Async::Barrier#async} to create multiple child tasks, and wait for them all to complete using {ruby Async::Barrier#wait}.
128128

129129
```ruby
130130
barrier = Async::Barrier.new
@@ -183,9 +183,9 @@ barrier.wait
183183

184184
## Stopping a Task
185185

186-
When a task completes execution, it will enter
186+
When a task completes execution, it will enter the `complete` state (or the `failed` state if it raises an unhandled exception).
187187

188-
There are various situations where you may want to stop a task ({ruby Async::Task#stop}). The most common case is shutting down a server, but other important situations exist, e.g. you may fan out multiple (10s, 100s) of requests, wait for a subset to complete (e.g. the first 5 or all those that complete within a given deadline), and then stop (terminate/cancel) the remaining operations.
188+
There are various situations where you may want to stop a task ({ruby Async::Task#stop}) before it completes. The most common case is shutting down a server, but other important situations exist, e.g. you may fan out multiple (10s, 100s) of requests, wait for a subset to complete (e.g. the first 5 or all those that complete within a given deadline), and then stop (terminate/cancel) the remaining operations.
189189

190190
Using the above program as an example, we can
191191

@@ -205,7 +205,7 @@ end
205205

206206
### Stopping all Tasks held in a Barrier
207207

208-
To stop (terminate/cancel) the all tasks held in a barrier:
208+
To stop (terminate/cancel) all the tasks held in a barrier:
209209

210210
```ruby
211211
barrier = Async::Barrier.new
@@ -222,7 +222,7 @@ Async do
222222
end
223223
```
224224

225-
If you're letting individual tasks held by a barrier 8000 throw unhandled exceptions, be sure to call ({ruby Async::Barrier#stop}):
225+
If you're letting individual tasks held by a barrier raise unhandled exceptions, be sure to call ({ruby Async::Barrier#stop}) to stop the remaining tasks:
226226

227227
```ruby
228228
barrier = Async::Barrier.new
@@ -300,57 +300,59 @@ end
300300

301301
## Timeouts
302302

303-
You can wrap asynchronous operations in a timeout. This ensures that malicious services don't cause your code to block indefinitely.
303+
You can wrap asynchronous operations in a timeout. This allows you to put an upper bound on how long the enclosed code will run vs. potentially blocking indefinitely. If the enclosed code hasn't completed by the timeout, it will be interrupted with an {ruby Async::TimeoutError} exception.
304304

305305
~~~ ruby
306306
require 'async'
307307

308308
Async do |task|
309309
task.with_timeout(1) do
310-
task.sleep 100
310+
sleep 100
311311
rescue Async::TimeoutError
312-
puts "I timed out!"
312+
puts "I timed out 99 seconds early!"
313313
end
314314
end
315315
~~~
316316

317-
### Reoccurring Timers
317+
### Periodic Timers
318318

319-
Sometimes you need to do some periodic work in a loop.
319+
Sometimes you need to do some recurring work in a loop. Often it's best to measure the periodic delay end-to-start, so that your process always takes a break between iterations and doesn't risk spending 100% of its time on the periodic work. In this case, simply call {ruby sleep} between iterations:
320320

321321
~~~ ruby
322322
require 'async'
323323

324324
Async do |task|
325-
while true
325+
loop do
326326
puts Time.now
327-
task.sleep 1
327+
# ... process job ...
328+
sleep 1
328329
end
329330
end
330331
~~~
331332

332333
## Reactor Lifecycle
333334

334-
Generally, the event loop will not exit until all tasks complete. This is informed by {ruby Async::Task#finished?} which checks if the current node has completed execution, which also includes all children. However, there is one exception to this rule: tasks flagged as being transient ({ruby Async::Node#transient?}).
335+
Generally, the outer reactor will not exit until all tasks complete. This is informed by {ruby Async::Task#finished?} which checks if the current node has completed execution, which also includes all children. However, there is one exception to this rule: tasks flagged as being `transient` ({ruby Async::Node#transient?}).
335336

336337
### Transient Tasks
337338

338-
Tasks which are flagged as transient, do not behave like normal tasks.
339+
Tasks which are flagged as `transient` do not behave like normal tasks.
339340

340341
```ruby
341342
@pruner = Async(transient: true) do
342-
while true
343+
loop do
344+
343345
sleep 1
344346
prune_connection_pool
345347
end
346348
end
347349
```
348350

349-
1. They will not keep the reactor alive, and instead are stopped (with a {ruby Async::Stop} exception) when all other (non-transient) tasks are finished.
350-
2. If the parent task is finished, any transient tasks will become children of the parent's parent, i.e. they don't keep sub-trees alive.
351-
3. If you stop a task which has transient children, those transient children will not be stopped and will instead move up the tree.
351+
1. They are not considered by {ruby Async::Task#finished?}, so they will not keep the reactor alive. Instead, they are stopped (with a {ruby Async::Stop} exception) when all other (non-transient) tasks are finished.
352+
2. As soon as a parent task is finished, any transient child tasks will be moved up to be children of the parent's parent. This ensures that they never keep a sub-tree alive.
353+
3. Similarly, if you `stop` a task, any transient child tasks will be moved up the tree as above rather than being stopped.
352354

353-
The purpose of transient tasks is when a task is an implementation detail of an object or instance, rather than a concurrency process. Some examples of trasient tasks
355+
The purpose of transient tasks is when a task is an implementation detail of an object or instance, rather than a concurrency process. Some examples of transient tasks:
354356

355357
- A task which is reading or writing data on behalf of a stateful connection object, e.g. HTTP/2 frame reader, Redis cache invalidation, etc.
356358
- A task which is monitoring and maintaining a connection pool, pruning unused connections or possibly ensuring those connections are periodically checked for activity (ping/pong, etc).
@@ -364,21 +366,23 @@ require 'async'
364366
require 'thread/local' # thread-local gem.
365367

366368
class TimeCache
367-
extend Thread::Local
369+
extend Thread::Local # defines `instance` class method that lazy-creates a separate instance per thread
368370

369371
def initialize
370372
@current_time = nil
371373
end
372374

373-
def current_time
375+
def current_time_string
374376
refresh!
375377

376378
return @current_time
377379
end
378380

379-
private def refresh!
381+
private
382+
383+
def refresh!
380384
@refresh ||= Async(transient: true) do
381-
while true
385+
loop do
382386
@current_time = Time.now.to_s
383387
sleep(1)
384388
end
@@ -390,9 +394,9 @@ class TimeCache
390394
end
391395

392396
Async do
393-
# If you are handling 1000s of requests per second, it can be an advantage to cache the current time as a string.
394-
p TimeCache.instance.current_time
397+
# If you are handling 1000s of requests per second, it can be an advantage to cache the current time string.
398+
p TimeCache.instance.current_time_string
395399
end
396400
```
397401

398-
Upon existing the top level async block, the {ruby @refresh} task will be set to `nil`. Bear in mind, you should not share these resources across threads, doing so would need further locking/coordination.
402+
Upon existing the top level async block, the {ruby @refresh} task will be set to `nil`. Bear in mind, you should not share these resources across threads; doing so would need some form of mutual exclusion.

0 commit comments

Comments
 (0)
0