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

View File

@ -301,45 +301,64 @@ effects on forward declarations, callbacks and methods using
### Cancellation support ### Cancellation support
Any running `Future` can be cancelled. This can be used to launch multiple Any running `Future` can be cancelled. This can be used for timeouts,
futures, and wait for one of them to finish, and cancel the rest of them, to let a user cancel a running task, to start multiple futures in parallel
to add timeout, or to let the user cancel a running task. and cancel them as soon as one finishes, etc.
```nim ```nim
# Simple cancellation import chronos/apps/http/httpclient
let future = sleepAsync(10.minutes)
future.cancel()
# Wait for cancellation proc cancellationExample() {.async.} =
let future2 = sleepAsync(10.minutes) # Simple cancellation
await future2.cancelAndWait() 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 # Wait for cancellation
proc retrievePage(uri: string): Future[string] {.async.} = let future2 = sleepAsync(10.minutes)
# requires to import uri, chronos/apps/http/httpclient, stew/byteutils await future2.cancelAndWait()
let httpSession = HttpSessionRef.new() # Using `cancelAndWait`, we know that future2 isn't
try: # pending anymore. However, it could have completed
resp = await httpSession.fetch(parseUri(uri)) # before cancellation happened (in which case, it
result = string.fromBytes(resp.data) # will hold a value)
finally:
# be sure to always close the session
await httpSession.closeWait()
let # Race between futures
futs = proc retrievePage(uri: string): Future[string] {.async.} =
@[ let httpSession = HttpSessionRef.new()
retrievePage("https://duckduckgo.com/?q=chronos"), try:
retrievePage("https://www.google.fr/search?q=chronos") 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) let
for fut in futs: futs =
if not fut.finished: @[
fut.cancel() retrievePage("https://duckduckgo.com/?q=chronos"),
echo "Result: ", await finishedFut 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 ```nim
proc c1 {.async.} = proc c1 {.async.} =
echo "Before sleep" echo "Before sleep"