Seems like we have to live with async, let's think if we can improve how it's used. I used Nim async and it looks to me, compared to the experience of Async in JavaScript, there are couple of things could be improved.
1 Use same await instead of wait_for if async needs to be called in sync function.
2 No sync IO instead of async proc allowed, mark all non-async IO with syncio and throw error if it's used in async proc. It is ok to block async event loop with CPU, it terrible if it's blocked with sync-IO.
3 Allow implicit return from async functions.
4 Allow to return future from async function.
5 Rename async_check as spawn(Future) or check(Future).
6 Remove run_forewer, if there's at least one listener registered on the event loop keep process running.
7 Don't print ugly multi-page async error stack traces, if it can't be made short and clean it's better to not print it at all, as they are usually useless anyway.
8 with_timeout should throw error or return Future[T], not Future[bool].
9 Maybe Stop using {.async.} the code is noisy, if function returns Future it's async.
What do you think? If we have to live with it, let's make it great! :)
None of it works right now:
a)
proc a: Future[int] = 10
b)
proc a: Future[int] {.async.} = 10
c)
proc a: Future[int] {.async.} = return 10
proc b: Future[int] {.async.} = a()
d)
proc somefn1: Future[int] {.async.} = return 10
proc somefn2: Future[int] {.async.} = return somefn1()
nim c -r nodem/greeting_example/user.nim
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(29) callNimAsyncContinue
/alex/projects/nim/nodem/nodem/netm.nim(120) callIter
[[reraised from:
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(29) call_nimported_functionNimAsyncContinue
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(145) call_nimported_functionIter
/balex/applications/nim-1.4.2/lib/pure/asyncfutures.nim(372) read
]]
[[reraised from:
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(29) call_nimported_functionNimAsyncContinue
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(145) call_nimported_functionIter
/balex/applications/nim-1.4.2/lib/pure/asyncfutures.nim(372) read
]]
[[reraised from:
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(29) say_hiNimAsyncContinue
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(145) say_hiIter
/balex/applications/nim-1.4.2/lib/pure/asyncfutures.nim(372) read
]]
[[reraised from:
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(29) selfNimAsyncContinue
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(145) selfIter
/balex/applications/nim-1.4.2/lib/pure/asyncfutures.nim(372) read
]]
[[reraised from:
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncfutures.nim(420) asyncCheckCallback
]]
Error: unhandled exception: Timed out, connection closed
Async traceback:
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(29) callNimAsyncContinue
/alex/projects/nim/nodem/nodem/netm.nim(120) callIter
#[
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(29) call_nimported_functionNimAsyncContinue
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(145) call_nimported_functionIter
/balex/applications/nim-1.4.2/lib/pure/asyncfutures.nim(372) read
]#
#[
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(29) call_nimported_functionNimAsyncContinue
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(145) call_nimported_functionIter
/balex/applications/nim-1.4.2/lib/pure/asyncfutures.nim(372) read
]#
#[
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(29) say_hiNimAsyncContinue
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(145) say_hiIter
/balex/applications/nim-1.4.2/lib/pure/asyncfutures.nim(372) read
]#
#[
/alex/projects/nim/nodem/greeting_example/user.nim(14) user
/alex/projects/nim/nodem/nodem.nim(359) run
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1935) waitFor
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1627) poll
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(1368) runOnce
/balex/applications/nim-1.4.2/lib/pure/asyncdispatch.nim(208) processPendingCallbacks
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(29) selfNimAsyncContinue
/balex/applications/nim-1.4.2/lib/pure/asyncmacro.nim(145) selfIter
/balex/applications/nim-1.4.2/lib/pure/asyncfutures.nim(372) read
]#
Exception message: Timed out, connection closed
Exception type: [Exception]
Error: execution of an external program failed: '/alex/projects/nim/build/user '
P.S.
Eventually though, I hope Nim deprecate async and use better approaches, async is too low level and designed for machines, not humans, working with async is like writting C by hands.
The future lies in https://github.com/disruptek/cps and https://github.com/mratsim/weave . I've said it many times, if CPS requires compiler support to be really usable, so be it. Please join these efforts!
Yes, these are long-term strategic goals. My suggestions are more like short-term tactical improvements :)
To clarify my remarks:
Async means lots of things:
CPS is an alternative to our closure iterators and all the rest could remain the same or be improved independently.
Thanks for writing up this list!
I agree with a lot of the items you've listed. In particular I think the following should be prioritised:
5 Don't print multi-page async error stack traces, if it can't be made short and clean maybe it's better to trim it somehow.
6 No sync IO instead of async proc allowed, mark all non-async IO with syncio and throw error if it's used in async proc. It is ok to block async event loop with CPU, it's terrible if it's blocked with sync-IO.
If you have the time I'm always happy to help answer any questions about how to approach these and review any PRs for them. I'd love to see these improvements :)
@alexeypetrushin did you researched for effect handlers?
Good: remove de async pollution of procedural codes parts
Bad: now you have not only blue/red function you all color spectrum
Not sure how much far from continuations it is.
I'd bet it is implemented using continuations but with name focused on developer interface instead of how is implemented.
sources:
https://www.stephendiehl.com/posts/exotic03.html
https://koka-lang.github.io/koka/doc/book.html#why-handlers
I'm almost sure that I saw some other content about this with some code like above
Syntax approximation: Used raise, catch and continue to denote keywords and more or less how this flow work.
type async = effect
proc itWillCallBlockingStuff(): int =
# ffi lib developers
# we thrown some effect
let value = raise async, calling_C_BlockingStuff()
return value + 1
proc usualOldStyleCode(): int =
# it doesn't need to be polluted with async syntax
# Hope most usual case
let value = itWillCallBlockingStuff()
return value + 1
proc parallelizeStuff(): int =
# only if you need some kind o optimization
let effect1 = catch async, usualOldStyleStuff()
let effect2 = catch async, usualOldStyleStuff()
let values = raise async, all(effect1, effect2)
# let value = raise async, first(effect1, effect2)
return sum(values) + 1
when isMainModule:
# won't compile, unhandled effect: 'async' ...
echo parallelizeStuff()
# sync runner
let process = catchAll async, parallelizeStuff()
for v in process:
let r = run(v)
continue v, r
echo process.result()
# parallel implementation
let process = catchAll async, parallelizeStuff()
parallel:
for v in process:
let r = spawn run(v)
continue v, r
echo process.result()