Godot version:
Custom build from 8d15d2323
OS/device including version:
Windows 10, Linux MX
Issue description:
I will explain this as i understand it:
signal processing_completed()
var list: Dictionary = {}
func myFunc1():
myFunc2()
yield(self, "processing_completed")
# Do more stuff
func myFunc2():
# yield(get_tree(), "idle_frame") # Adding that also works
# If list is empty, this function returns immediately. If not, it processes
for key in list:
http_request_object.request(key) # This is asynchronous and we have to wait for it.
var result = yield(http_request_object, "request_completed")
# Do stuff with the result...
emit_signal("processing_completed") # This does NOT work if the list is empty
call_deferred("emit_signal", "processing_completed") # This works even if the list is empty
In the above example, if the list variable is not empty AND processing the list takes more than one frame (to my understanding) then yield will capture the signal and continue. Otherwise, if the list is empty and the for loop returns immediately then the process_completed signal is never captured and yield waits forever. Doing call_deferred("emit_signal", "processing_completed") seems to work also even if the list is empty (hence the same frame logic). Luckily, i ended up with an empty list by accident and figured this out. Otherwise, with a released game this could be a mess to figure out.
var list = []
func myFunc1():
call_deferred("myFunc2")
yield(self, "processing_completed")
print("2")
func myFunc2():
print("1")
for key in list:
print("list item")
emit_signal("processing_completed")
1
2
but this is unnecessary even, it's easier just like that
var list = []
func myFunc1():
myFunc2()
print("2")
func myFunc2():
print("1")
for key in list:
print("list item")
;)
var list = [] func myFunc1(): call_deferred("myFunc2") yield(self, "processing_completed") print("2") func myFunc2(): print("1") for key in list: print("list item") emit_signal("processing_completed")1
2but this is unnecessary even, it's easier just like that
var list = [] func myFunc1(): myFunc2() print("2") func myFunc2(): print("1") for key in list: print("list item");)
I understand the confusion. Edited my answer so that it makes more sense as to why i need the yield ;)
It seems to me that this combination should give the desired result.
func myFunc1():
...
call_deferred("myFunc2")
yield(self, "processing_completed")
...
It seems to me that this combination should give the desired result.
call_deferred("myFunc2") yield(self, "processing_completed")
Yes indeed, that is pretty much the same idea as:
call_deferred("emit_signal", "processing_completed") # This works even if the list is empty.
That is the reason i created the issue.
In my case it should not be sensitive to whether there is something in the "list" or not.
I don鈥檛 think this is a bug, so most likely the issue will be closed.
In my case it should not be sensitive to whether there is something in the "list" or not.
I don鈥檛 think this is a bug, so most likely the issue will be closed.
The expectation i had was for yield to continue when the signal is emitted regardless of the circumstances. Otherwise i could not have prevented the error in the game without knowing the info i issued. So it is either a bug OR if this is expected behavior it should be documented.
Yes it is, but there is one nuance =)
when entering the function (myFunc2), yield will not be executed, which is why the signal is not caught
func myFunc1():
---1] myFunc2()
| 6] yield(self, "processing_completed") # the signal was emitted before we began to catch it
| X] print("2")
|
\|/
func myFunc2():
2] print("1")
3] for key in list:
4] print("list item")
5] emit_signal("processing_completed")
Result:
Steps in my decision
func myFunc1():
1] call_deferred("myFunc2")
2] yield(self, "processing_completed") # OK
7] print("2")
func myFunc2():
3] print("1")
4] for key in list:
5] print("list item")
6] emit_signal("processing_completed")
Result:
1
2
This is not a bug, it is working as expected. You should yield for the function instead of a signal, if you want to wait for the function to complete:
var result = maybe_async()
if result is GDScriptFunctionState:
result = yield(result, "completed")
Godot will soon have the await keyword to simplify that down to await maybe_async().
I'v move away from yield for now and using states for more control. Even if this is a bug or expected behavior i understand that it is not worth bothering with it at this point if await is going to be implemented so i'm closing it.
Most helpful comment
This is not a bug, it is working as expected. You should yield for the function instead of a signal, if you want to wait for the function to complete:
Godot will soon have the
awaitkeyword to simplify that down toawait maybe_async().