Update README regarding cancellation (#450)

* Update README regarding cancellation

* Apply suggestions from code review

Co-authored-by: Eugene Kabanov <eugene.kabanov@status.im>

---------

Co-authored-by: Jacek Sieka <jacek@status.im>
Co-authored-by: Eugene Kabanov <eugene.kabanov@status.im>
This commit is contained in:
Tanguy 2023-10-25 15:16:10 +02:00 committed by GitHub
parent f56d286687
commit 12dc36cfee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 50 additions and 31 deletions

View File

@ -301,45 +301,64 @@ effects on forward declarations, callbacks and methods using
### Cancellation support
Any running `Future` can be cancelled. This can be used to launch multiple
futures, and wait for one of them to finish, and cancel the rest of them,
to add timeout, or to let the user cancel a running task.
Any running `Future` can be cancelled. This can be used for timeouts,
to let a user cancel a running task, to start multiple futures in parallel
and cancel them as soon as one finishes, etc.
```nim
# Simple cancellation
let future = sleepAsync(10.minutes)
future.cancel()
import chronos/apps/http/httpclient
# Wait for cancellation
let future2 = sleepAsync(10.minutes)
await future2.cancelAndWait()
proc cancellationExample() {.async.} =
# Simple cancellation
let future = sleepAsync(10.minutes)
future.cancelSoon()
# `cancelSoon` will not wait for the cancellation
# to be finished, so the Future could still be
# pending at this point.
# Race between futures
proc retrievePage(uri: string): Future[string] {.async.} =
# requires to import uri, chronos/apps/http/httpclient, stew/byteutils
let httpSession = HttpSessionRef.new()
try:
resp = await httpSession.fetch(parseUri(uri))
result = string.fromBytes(resp.data)
finally:
# be sure to always close the session
await httpSession.closeWait()
# Wait for cancellation
let future2 = sleepAsync(10.minutes)
await future2.cancelAndWait()
# Using `cancelAndWait`, we know that future2 isn't
# pending anymore. However, it could have completed
# before cancellation happened (in which case, it
# will hold a value)
let
futs =
@[
retrievePage("https://duckduckgo.com/?q=chronos"),
retrievePage("https://www.google.fr/search?q=chronos")
]
# Race between futures
proc retrievePage(uri: string): Future[string] {.async.} =
let httpSession = HttpSessionRef.new()
try:
let resp = await httpSession.fetch(parseUri(uri))
return bytesToString(resp.data)
finally:
# be sure to always close the session
# `finally` will run also during cancellation -
# `noCancel` ensures that `closeWait` doesn't get cancelled
await noCancel(httpSession.closeWait())
let finishedFut = await one(futs)
for fut in futs:
if not fut.finished:
fut.cancel()
echo "Result: ", await finishedFut
let
futs =
@[
retrievePage("https://duckduckgo.com/?q=chronos"),
retrievePage("https://www.google.fr/search?q=chronos")
]
let finishedFut = await one(futs)
for fut in futs:
if not fut.finished:
fut.cancelSoon()
echo "Result: ", await finishedFut
waitFor(cancellationExample())
```
When an `await` is cancelled, it will raise a `CancelledError`:
Even if cancellation is initiated, it is not guaranteed that
the operation gets cancelled - the future might still be completed
or fail depending on the ordering of events and the specifics of
the operation.
If the future indeed gets cancelled, `await` will raise a
`CancelledError` as is likely to happen in the following example:
```nim
proc c1 {.async.} =
echo "Before sleep"