I’m unsure how to get the thrown error or backtrace of an asynchronous Task.
Here’s an example script:
# silent_test_all_in_one.jl
function do_some_work(increase::Real)
a = 0.0
start_time = time()
while time() - start_time < 5
b += increase
sleep(0.1)
end
a
end
function start_blocking()
println("start() is waiting for do_some_work() to be done")
do_some_work(10)
end
function start_async()
start_time = time()
t = @async do_some_work(10)
while !istaskdone(t)
println("start() is waiting for do_some_work() to be done")
sleep(1)
end
end
To test, I did the following
Save that file as “silent_test_all_in_one.jl” and include it at the REPL
call start_blocking and see the appropriate error
call start_async and don’t see any indication of an error
julia> include("silent_test_all_in_one.jl")
start_async (generic function with 1 method)
julia> start_blocking()
start() is waiting for do_some_work() to be done
ERROR: UndefVarError: b not defined
Stacktrace:
[1] do_some_work at C:\Users\dave\Documents\silent_test_all_in_one.jl:7 [inlined]
[2] start_blocking() at C:\Users\dave\Documents\silent_test_all_in_one.jl:15
[3] top-level scope at none:0
julia> start_async()
start() is waiting for do_some_work() to be done
julia> versioninfo()
Julia Version 1.0.2
Commit d789231e99 (2018-11-08 20:11 UTC)
Platform Info:
OS: Windows (x86_64-w64-mingw32)
CPU: Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-6.0.0 (ORCJIT, haswell)
Is this expected? Can this be avoided?
I’m working on a project that uses HTTP.jl to handle some requests asynchronously with Tasks. Often, the code “hangs” in the REPL and gives no feedback and that’s when I realized that asynchronous Taskss don’t seem to print out thrown errors or backtraces when the code actually errors out.
You can either print the error yourself or look at what the task resolves to:
function start_async_print()
start_time = time()
t = @async try
do_some_work(10)
catch err
bt = catch_backtrace()
println()
showerror(stderr, err, bt)
end
while !istaskdone(t)
println("start() is waiting for do_some_work() to be done")
sleep(1)
end
end
function start_async_throw()
start_time = time()
t = do_some_work(10)
while !istaskdone(t)
println("start() is waiting for do_some_work() to be done")
sleep(1)
end
wait(t) # or fetch(t)
end
gives
julia> start_async_print()
start() is waiting for do_some_work() to be done
UndefVarError: b not defined
Stacktrace:
[1] do_some_work at ./untitled-8822451613b5304f5f50924dadde5d27:6 [inlined]
[2] (::getfield(Main, Symbol("##27#28")))() at ./task.jl:259
julia> start_async_throw()
ERROR: UndefVarError: b not defined
Stacktrace:
[1] do_some_work at ./untitled-8822451613b5304f5f50924dadde5d27:6 [inlined]
[2] start_async_throw() at ./untitled-8822451613b5304f5f50924dadde5d27:33
[3] top-level scope at none:0
The wait/fetch approach obviously doesn’t work very well when you don’t want to block.
@pfitzseb I’m curious why @async doesn’t do this automatically and I’m trying to imagine the downsides.
This seems like an easily avoidable trap for newbies. For more advanced users who want the current behavior explicitly, they could call a macro like @async_silent.
This is an old topic, but I have the very same issue with async tasks: they don’t show a useful stacktrace be default. Not only @async itself, but also e.g. asyncmap:
asyncmap(0:10) do i
1 ÷ i
end
ERROR: DivideError: integer division error
Stacktrace:
[1] (::Base.var"#770#772")(::Task) at ./asyncmap.jl:178
[2] foreach(::Base.var"#770#772", ::Array{Any,1}) at ./abstractarray.jl:2009
[3] maptwice(::Function, ::Channel{Any}, ::Array{Any,1}, ::UnitRange{Int64}) at ./asyncmap.jl:178
[4] wrap_n_exec_twice at ./asyncmap.jl:154 [inlined]
[5] #async_usemap#755 at ./asyncmap.jl:103 [inlined]
[6] #asyncmap#754 at ./asyncmap.jl:81 [inlined]
[7] asyncmap(::Function, ::UnitRange{Int64}) at ./asyncmap.jl:81
[8] top-level scope at REPL[3]:1
compare to map:
map(0:10) do i
1 ÷ i
end
ERROR: DivideError: integer division error
Stacktrace:
[1] div at ./int.jl:260 [inlined]
[2] #13 at ./REPL[4]:2 [inlined]
[3] iterate at ./generator.jl:47 [inlined]
[4] _collect(::UnitRange{Int64}, ::Base.Generator{UnitRange{Int64},var"#13#14"}, ::Base.EltypeUnknown, ::Base.HasShape{1}) at ./array.jl:699
[5] collect_similar(::UnitRange{Int64}, ::Base.Generator{UnitRange{Int64},var"#13#14"}) at ./array.jl:628
[6] map(::Function, ::UnitRange{Int64}) at ./abstractarray.jl:2162
[7] top-level scope at REPL[4]:1
Is there any way to force the stacktrace go further than the asyncmap internals, i.e. to the actual user function? I can only imagine putting try ... catch ... end there manually.