Another newbie question :-(
I'm still having difficulty in using async callback to update future result with computed data (a time stamp in this case).
# - How can I get access to the completion time (info.finish) when the request
# has been fullfilled?
# - My guess is that in the callback *info* is being *passed by value* so the timestamp
# (info.finish) isn't being updated ( and is returned as zero :-( )
import std/[algorithm, asyncdispatch, httpclient, strutils, times]
var startTime: Time
type URLinfo = object
start: Time
finish: Time
url: string
content: string
proc duration(info: URLinfo): Duration =
info.finish - info.start
proc startOffset(info: URLinfo) : Duration = # relative timestamp
info.start - startTime
proc `$`(info: URLinfo): string =
"start " & align($(info.startOffset.inMilliseconds), 4) & " mSec " &
info.url & " yielded " & $(info.content.len) &
" bytes in " & $(info.duration.inMicroseconds) & " uSec"
proc fetch(url: string): Future[URLinfo] {.async.} =
let
client = newAsyncHttpClient()
future = await client.getContent url
result = URLInfo(start: getTime(), url: url, content: future)
proc getURLs(urls: seq[string]) : Future[seq[URLinfo]] {.async.} =
var futures: seq[Future[URLinfo]]
startTime = getTime()
echo "\nCorrect durations are displayed:"
for url in urls:
var future = fetch url
future.addCallback(
proc(future: Future[URLinfo]) =
var info = future.read
info.finish = getTime() # Doesn't update original URLinfo :-(
echo $info
)
futures.add future
result = await all futures
echo "\nWrong durations are displayed:"
for entry in result.sortedByIt(it.startOffset): echo entry
proc main() {.async} =
var sites = @[
"https://amazon.com",
"https://facebook.com",
"https://google.com",
"https://nim-lang.org",
"https://reddit.com",
"https://twitter.com",
"https://youtube.com",
]
echo "Main Finshed: ", (await getURLs sites).len, " sites"
waitFor main()
➜ nim git:(master) ✗ nim r -d:ssl example.nim
Correct durations are displayed:
start 279 mSec https://nim-lang.org yielded 42406 bytes in 50 uSec
start 409 mSec https://google.com yielded 14689 bytes in 19 uSec
start 496 mSec https://twitter.com yielded 93713 bytes in 1356 uSec
start 531 mSec https://facebook.com yielded 80313 bytes in 40 uSec
start 654 mSec https://youtube.com yielded 568834 bytes in 72 uSec
start 713 mSec https://amazon.com yielded 521418 bytes in 435 uSec
start 1086 mSec https://reddit.com yielded 795122 bytes in 733 uSec
Wrong durations are displayed:
start 279 mSec https://nim-lang.org yielded 42406 bytes in -1646075063812694 uSec
start 409 mSec https://google.com yielded 14689 bytes in -1646075063942829 uSec
start 496 mSec https://twitter.com yielded 93713 bytes in -1646075064030065 uSec
start 531 mSec https://facebook.com yielded 80313 bytes in -1646075064064514 uSec
start 654 mSec https://youtube.com yielded 568834 bytes in -1646075064187285 uSec
start 713 mSec https://amazon.com yielded 521418 bytes in -1646075064246545 uSec
start 1086 mSec https://reddit.com yielded 795122 bytes in -1646075064619413 uSec
Main Finshed: 7 sites
How should it work? You didn't give it a mechanism to edit the original URLInfo or return it from the proc. It's passed by value.
This works though. Note the times are slightly off because the finish time is in slightly different places.
# - How can I get access to the completion time (info.finish) when the request
# has been fullfilled?
# - My guess is that in the callback *info* is being *passed by value* so the timestamp
# (info.finish) isn't being updated ( and is returned as zero :-( )
import std/[algorithm, asyncdispatch, httpclient, strutils, times]
var startTime: Time
type URLinfo = object
start: Time
finish: Time
url: string
content: string
proc duration(info: URLinfo): Duration =
info.finish - info.start
proc startOffset(info: URLinfo) : Duration = # relative timestamp
info.start - startTime
proc `$`(info: URLinfo): string =
"start " & align($(info.startOffset.inMilliseconds), 4) & " mSec " &
info.url & " yielded " & $(info.content.len) &
" bytes in " & $(info.duration.inMicroseconds) & " uSec"
proc fetch(url: string): Future[URLinfo] {.async.} =
let
startTime = getTime()
client = newAsyncHttpClient()
future = await client.getContent url
result = URLInfo(start: startTime, finish: getTime(), url: url, content: future)
proc getURLs(urls: seq[string]) : Future[seq[URLinfo]] {.async.} =
var futures: seq[Future[URLinfo]]
startTime = getTime()
echo "\nCorrect durations are displayed:"
for url in urls:
var future = fetch url
future.addCallback(
proc(future: Future[URLinfo]) =
var info = future.read
info.finish = getTime() # Doesn't update original URLinfo :-(
echo $info
)
futures.add future
result = await all futures
echo "\nWrong durations are displayed:"
for entry in result.sortedByIt(it.startOffset): echo entry
proc main() {.async} =
var sites = @[
"https://amazon.com",
"https://facebook.com",
"https://google.com",
"https://nim-lang.org",
"https://reddit.com",
"https://twitter.com",
"https://youtube.com",
]
echo "Main Finshed: ", (await getURLs sites).len, " sites"
waitFor main()
Thanks... that is pretty much what I suspected...
I was hoping to collect the finish time in the callback .... but after the await is probably just as good.
That seems to work!
# - How can I get access to the completion time (info.finish) when the request
# has been fullfilled?
# - My guess is that in the callback *info* is being *passed by value* so the timestamp
# (info.finish) isn't being updated ( and is returned as zero :-( )
import std/[algorithm, asyncdispatch, httpclient, strutils, times]
var startTime: Time
type URLinfo = ref object
start: Time
finish: Time
url: string
content: string
proc duration(info: URLinfo): Duration =
info.finish - info.start
proc startOffset(info: URLinfo) : Duration = # relative timestamp
info.start - startTime
proc `$`(info: URLinfo): string =
"start " & align($(info.startOffset.inMilliseconds), 4) & " mSec " &
info.url & " yielded " & $(info.content.len) &
" bytes in " & $(info.duration.inMicroseconds) & " uSec"
proc fetch(url: string): Future[URLinfo] {.async.} =
let
startTime = getTime()
client = newAsyncHttpClient()
result = URLInfo(start: startTime, url: url)
result.content = await client.getContent url
proc getURLs(urls: seq[string]) : Future[seq[URLinfo]] {.async.} =
var futures: seq[Future[URLinfo]]
startTime = getTime()
echo "\nCorrect durations are displayed:"
for url in urls:
var future = fetch url
future.addCallback(
proc(future: Future[URLinfo]) =
var info = future.read
info.finish = getTime() # Doesn't update original URLinfo :-(
echo $info
)
futures.add future
result = await all futures
echo "\nWrong durations are displayed:"
for entry in result.sortedByIt(it.startOffset): echo entry
proc main() {.async} =
var sites = @[
"https://amazon.com",
"https://facebook.com",
"https://google.com",
"https://nim-lang.org",
"https://reddit.com",
"https://twitter.com",
"https://youtube.com",
]
echo "Main Finshed: ", (await getURLs sites).len, " sites"
waitFor main()