mafredri / zsh-async

Because your terminal should be able to perform tasks asynchronously without external tools!
MIT License
756 stars 37 forks source link

unexpected incompatibility when used in .zshrc? #34

Closed mscottnelson closed 4 years ago

mscottnelson commented 4 years ago

It's possible this is my own ignorance.

From an otherwise entirely empty .zshrc, if I call ./example.zsh (filled with a copy of the listing in your README) it performs as expected.

However, if I inline that same script, it appears to get stuck in the while loop and the async functions never run. In the larger context of my existing zshrc, The async_job and it's callback run, but they occurred out of order.

Thank you!

mafredri commented 4 years ago

Could you post a sample code that reproduces the issue?

The async_job and it's callback run, but they occurred out of order.

Could you elaborate on this? I might misunderstand but there's no guarantee of execution order when using async tasks.

mscottnelson commented 4 years ago

This is specifically in reference to running the following code from my .zshrc. It does not occur when run in other scenarios. When I run the following code from .zshrc, the while loop never exits (ie output is "Waiting...Waiting...Waiting...Waiting...Waiting...Waiting...Waiting...Waiting...Waiting...Waiting...Waiting..."):

# .zshrc
#!/usr/bin/env zsh
source ./async.zsh
async_init

# Initialize a new worker (with notify option)
async_start_worker my_worker -n

# Create a callback function to process results
COMPLETED=0
completed_callback() {
    COMPLETED=$(( COMPLETED + 1 ))
    print $@
}

# Register callback function for the workers completed jobs
async_register_callback my_worker completed_callback

# Give the worker some tasks to perform
async_job my_worker print hello
async_job my_worker sleep 0.3

# Wait for the two tasks to be completed
while (( COMPLETED < 2 )); do
    print "Waiting..."
    sleep 0.1
done

print "Completed $COMPLETED tasks!"

# Output (expected):
#   Waiting...
#   print 0 hello 0.001583099365234375
#   Waiting...
#   Waiting...
#   sleep 0 0.30631208419799805
#   Completed 2 tasks!
# Output (actual):
#   Waiting...
#   Waiting...
#   Waiting...
#   Waiting...
#   Waiting...
#   Waiting...
#   Waiting...

However, if I save the same above code to a file, say, ~/example.sh and then, in my .zshrc, call it this way:

# .zshrc
./example.sh

then it executes as described in the example.

From other experiments, it seems that in this context, for me, the callback is being run before the async job. I do not expect async code to have a guaranteed execution order, but I do expect the callback to reliably run only upon completion of the job that is supposed to trigger the callback on completion.

Could there possibly be something unique to my context that is causing this? I am currently using oh-my-zsh (although I am considering abandoning it).

mscottnelson commented 4 years ago

Just want to clarify that in the scenario I describe above, there is absolutely nothing else in my .zshrc file but the example code.

mafredri commented 4 years ago

Hey @mscottnelson, somehow this dropped off my radar, sorry about that.

So the example script is not designed to be sourced as part of an interactive shell, which is what happens when it's put in .zshrc.

The reason it doesn't work is that zsh-async detects that the shell is interactive, has ZLE enabled and decides to use a ZLE watcher (zle -F) to detect when an async task is done. However, a ZLE watcher does not run until ZLE is active, which happens when the prompt is visible and no command is running. Since that while loop is running before the prompt becomes visible, zsh-async has no way to automatically detect that a task is complete.

You can fix the issue by manually checking the results in the while loop (async_process_results my_worker completed_callback).