├── .gitignore ├── icon.png ├── .gitattributes ├── addons └── godot-nesink │ ├── plugin.gd │ ├── plugin.cfg │ ├── NesinkronaDeferIdleAsync.gd │ ├── NesinkronaFromAsync.gd │ ├── NesinkronaCanceledAsync.gd │ ├── NesinkronaDeferPhysicsFrameAsync.gd │ ├── NesinkronaDeferProcessFrameAsync.gd │ ├── NesinkronaCompletedAsync.gd │ ├── NesinkronaDelayAsync.gd │ ├── NesinkronaThenAsync.gd │ ├── NesinkronaFromCallbackAsync.gd │ ├── NesinkronaUnwrapAsync.gd │ ├── NesinkronaRaceAsync.gd │ ├── NesinkronaAnyAsync.gd │ ├── NesinkronaThenCallbackAsync.gd │ ├── NesinkronaAllAsync.gd │ ├── NesinkronaAllSettledAsync.gd │ ├── Cancel.gd │ ├── NesinkronaFromSignalAsync.gd │ ├── NesinkronaFromSignalNameAsync.gd │ ├── NesinkronaAsyncBase.gd │ ├── NesinkronaConditionalSignalNameAsync.gd │ ├── NesinkronaConditionalSignalAsync.gd │ └── Async.gd ├── project.godot ├── icon.png.import ├── LICENSE.md ├── README_zh.md ├── README_ja.md ├── README.md ├── Test_Task.tscn └── Test_Task.gd /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/folt-a/godot-nesink/HEAD/icon.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /addons/godot-nesink/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | #------------------------------------------------------------------------------- 5 | 6 | func _get_plugin_name() -> String: 7 | return "Nesinkrona" 8 | -------------------------------------------------------------------------------- /addons/godot-nesink/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Nesinkrona" 4 | description="Pseudo (task based) asynchronous patterns for Godot 4." 5 | author="Ydi (@ydipeepo), @folt-a" 6 | version="1.0.2" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaDeferIdleAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaDeferIdleAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | 5 | func _init() -> void: 6 | _on_completed.call_deferred() 7 | 8 | func _on_completed() -> void: 9 | if is_pending: 10 | complete_release(null) 11 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaFromAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaFromAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | 5 | func _init(coroutine: Callable) -> void: 6 | _init_core(coroutine) 7 | 8 | func _init_core(coroutine: Callable) -> void: 9 | reference() 10 | complete_release(await coroutine.call()) 11 | unreference() 12 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaCanceledAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaCanceledAsync extends Async 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | # メソッド 5 | #--------------------------------------------------------------------------------------------------- 6 | 7 | func wait(cancel: Cancel = null) -> Variant: 8 | return null 9 | 10 | func get_state() -> int: 11 | return STATE_CANCELED 12 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaDeferPhysicsFrameAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaDeferPhysicsFrameAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | 5 | func _init() -> void: 6 | get_tree().physics_frame.connect(_on_completed) 7 | 8 | func _on_completed() -> void: 9 | get_tree().physics_frame.disconnect(_on_completed) 10 | if is_pending: 11 | complete_release(null) 12 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaDeferProcessFrameAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaDeferProcessFrameAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | 5 | func _init() -> void: 6 | get_tree().process_frame.connect(_on_completed) 7 | 8 | func _on_completed() -> void: 9 | get_tree().process_frame.disconnect(_on_completed) 10 | if is_pending: 11 | complete_release(null) 12 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaCompletedAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaCompletedAsync extends Async 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | # メソッド 5 | #--------------------------------------------------------------------------------------------------- 6 | 7 | func wait(cancel: Cancel = null) -> Variant: 8 | return _result 9 | 10 | func get_state() -> int: 11 | return STATE_COMPLETED 12 | 13 | #--------------------------------------------------------------------------------------------------- 14 | 15 | var _result: Variant 16 | 17 | func _init(result: Variant) -> void: 18 | _result = result 19 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="godot-nesink" 14 | config/description="Pseudo (task based) asynchronous patterns for Godot 4." 15 | run/main_scene="res://Test_Task.tscn" 16 | config/features=PackedStringArray("4.3") 17 | 18 | [editor_plugins] 19 | 20 | enabled=PackedStringArray("res://addons/godot-nesink/plugin.cfg") 21 | 22 | [rendering] 23 | 24 | vulkan/rendering/back_end=1 25 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaDelayAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaDelayAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | # 定数 5 | #--------------------------------------------------------------------------------------------------- 6 | 7 | const MIN_TIMEOUT := 0.0 8 | 9 | #--------------------------------------------------------------------------------------------------- 10 | 11 | var _timeout: float 12 | 13 | func _init(timeout: float) -> void: 14 | assert(MIN_TIMEOUT <= timeout) 15 | 16 | _timeout = timeout 17 | get_tree().create_timer(timeout).timeout.connect(_on_timeout) 18 | 19 | func _on_timeout() -> void: 20 | complete_release(_timeout) 21 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaThenAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaThenAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | 5 | func _init(drain: Async, drain_cancel: Cancel, coroutine: Callable) -> void: 6 | assert(drain != null) 7 | 8 | _init_core(drain, drain_cancel, coroutine) 9 | 10 | func _init_core(drain: Async, drain_cancel: Cancel, coroutine: Callable) -> void: 11 | reference() 12 | var drain_result: Variant = await drain.wait(drain_cancel) 13 | match drain.get_state(): 14 | STATE_CANCELED: 15 | cancel_release() 16 | STATE_COMPLETED: 17 | complete_release(await coroutine.call(drain_result)) 18 | _: 19 | assert(false, "BUG") 20 | unreference() 21 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaFromCallbackAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaFromCallbackAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | 5 | var _pending := true 6 | 7 | func _init(coroutine: Callable, unbind_cancel: bool) -> void: 8 | if unbind_cancel: 9 | coroutine = coroutine.unbind(1) 10 | _init_core(coroutine) 11 | 12 | func _init_core(coroutine: Callable) -> void: 13 | reference() 14 | await coroutine.call(_complete_core, _cancel_core) 15 | 16 | func _complete_core(result: Variant = null) -> void: 17 | if _pending: 18 | _pending = false 19 | complete_release(result) 20 | unreference() 21 | 22 | func _cancel_core() -> void: 23 | if _pending: 24 | _pending = false 25 | cancel_release() 26 | unreference() 27 | -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bybb02k5ddtuj" 6 | path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.png" 14 | dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaUnwrapAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaUnwrapAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | 5 | func _init(drain: Async, drain_cancel: Cancel, depth: int) -> void: 6 | assert(0 < depth) 7 | assert(drain_cancel == null or not drain_cancel.is_requested) 8 | 9 | _init_core(drain, drain_cancel, depth) 10 | 11 | func _init_core(drain: Variant, drain_cancel: Cancel, depth: int) -> void: 12 | reference() 13 | var drain_result: Variant = await drain.wait(drain_cancel) 14 | while drain_result is Async and depth != 0: 15 | drain = drain_result 16 | drain_result = await drain.wait(drain_cancel) 17 | depth -= 1 18 | match drain.get_state(): 19 | STATE_CANCELED: 20 | cancel_release() 21 | STATE_COMPLETED: 22 | complete_release(drain_result) 23 | _: 24 | assert(false, "BUG") 25 | unreference() 26 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaRaceAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaRaceAsync extends NesinkronaAsyncBase 2 | 3 | #------------------------------------------------------------------------------- 4 | 5 | var _pending_drains: int 6 | 7 | func _init(drains: Array, drain_cancel: Cancel) -> void: 8 | assert(not drains.is_empty()) 9 | 10 | var drain_count := drains.size() 11 | 12 | _pending_drains = drain_count 13 | for drain_index: int in drain_count: 14 | _init_core( 15 | normalize_drain(drains[drain_index]), 16 | drain_cancel) 17 | 18 | func _init_core(drain: Variant, drain_cancel: Cancel) -> void: 19 | if drain is Async: 20 | reference() 21 | var drain_result: Variant = await drain.wait(drain_cancel) 22 | _pending_drains -= 1 23 | match drain.get_state(): 24 | STATE_CANCELED: 25 | if _pending_drains == 0: 26 | complete_release(NesinkronaCanceledAsync.new()) 27 | STATE_COMPLETED: 28 | complete_release(NesinkronaCompletedAsync.new(drain_result)) 29 | _: 30 | assert(false, "BUG") 31 | unreference() 32 | else: 33 | complete_release(NesinkronaCompletedAsync.new(drain)) 34 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaAnyAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaAnyAsync extends NesinkronaAsyncBase 2 | 3 | #------------------------------------------------------------------------------- 4 | 5 | var _pending_drains: int 6 | 7 | func _init(drains: Array, drain_cancel: Cancel) -> void: 8 | assert(not drains.is_empty()) 9 | assert(drain_cancel == null or not drain_cancel.is_requested) 10 | 11 | var drain_count := drains.size() 12 | 13 | _pending_drains = drain_count 14 | for drain_index: int in drain_count: 15 | _init_core( 16 | normalize_drain(drains[drain_index]), 17 | drain_cancel) 18 | 19 | func _init_core( 20 | drain: Variant, 21 | drain_cancel: Cancel) -> void: 22 | 23 | if drain is Async: 24 | reference() 25 | var drain_result: Variant = await drain.wait(drain_cancel) 26 | _pending_drains -= 1 27 | match drain.get_state(): 28 | STATE_CANCELED: 29 | if _pending_drains == 0: 30 | cancel_release() 31 | STATE_COMPLETED: 32 | complete_release(drain_result) 33 | _: 34 | assert(false, "BUG") 35 | unreference() 36 | else: 37 | _pending_drains -= 1 38 | complete_release(drain) 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2022 Ydi (@ydipeepo) and Nesinkrona contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaThenCallbackAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaThenCallbackAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | 5 | var _pending := true 6 | 7 | func _init( 8 | drain: Async, 9 | drain_cancel: Cancel, 10 | coroutine: Callable, 11 | unbind_cancel: bool) -> void: 12 | 13 | assert(drain != null) 14 | 15 | if unbind_cancel: 16 | coroutine = coroutine.unbind(1) 17 | _init_core(drain, drain_cancel, coroutine) 18 | 19 | func _init_core(drain: Async, drain_cancel: Cancel, coroutine: Callable) -> void: 20 | var drain_result: Variant = await drain.wait(drain_cancel) 21 | match drain.get_state(): 22 | STATE_CANCELED: 23 | cancel_release() 24 | _pending = true 25 | STATE_COMPLETED: 26 | reference() 27 | await coroutine.call(drain_result, _complete_core, _cancel_core) 28 | _: 29 | assert(false, "BUG") 30 | 31 | func _complete_core(result: Variant = null) -> void: 32 | if _pending: 33 | _pending = false 34 | complete_release(result) 35 | unreference() 36 | 37 | func _cancel_core() -> void: 38 | if _pending: 39 | _pending = false 40 | cancel_release() 41 | unreference() 42 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaAllAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaAllAsync extends NesinkronaAsyncBase 2 | 3 | #------------------------------------------------------------------------------- 4 | 5 | var _pending_drains: int 6 | 7 | func _init(drains: Array, drain_cancel: Cancel) -> void: 8 | assert(not drains.is_empty()) 9 | 10 | var drain_count := drains.size() 11 | var result := [] 12 | result.resize(drain_count) 13 | 14 | _pending_drains = drain_count 15 | for drain_index: int in drain_count: 16 | _init_core( 17 | normalize_drain(drains[drain_index]), 18 | drain_cancel, 19 | drain_index, 20 | result) 21 | 22 | func _init_core( 23 | drain: Variant, 24 | drain_cancel: Cancel, 25 | drain_index: int, 26 | result: Array) -> void: 27 | 28 | if drain is Async: 29 | reference() 30 | result[drain_index] = await drain.wait(drain_cancel) 31 | _pending_drains -= 1 32 | match drain.get_state(): 33 | STATE_CANCELED: 34 | cancel_release() 35 | STATE_COMPLETED: 36 | if _pending_drains == 0: 37 | complete_release(result) 38 | _: 39 | assert(false, "BUG") 40 | unreference() 41 | else: 42 | result[drain_index] = drain 43 | _pending_drains -= 1 44 | if _pending_drains == 0: 45 | complete_release(result) 46 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaAllSettledAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaAllSettledAsync extends NesinkronaAsyncBase 2 | 3 | #------------------------------------------------------------------------------- 4 | 5 | var _pending_drains: int 6 | 7 | func _init(drains: Array, drain_cancel: Cancel) -> void: 8 | assert(not drains.is_empty()) 9 | 10 | var drain_count := drains.size() 11 | var result := [] 12 | result.resize(drain_count) 13 | 14 | _pending_drains = drain_count 15 | for drain_index: int in drain_count: 16 | _init_core( 17 | normalize_drain(drains[drain_index]), 18 | drain_cancel, 19 | drain_index, 20 | result) 21 | 22 | func _init_core( 23 | drain: Variant, 24 | drain_cancel: Cancel, 25 | drain_index: int, 26 | result: Array) -> void: 27 | 28 | if drain is Async: 29 | reference() 30 | var drain_result: Variant = await drain.wait(drain_cancel) 31 | match drain.get_state(): 32 | STATE_CANCELED: 33 | result[drain_index] = NesinkronaCanceledAsync.new() 34 | STATE_COMPLETED: 35 | result[drain_index] = NesinkronaCompletedAsync.new(drain_result) 36 | _: 37 | assert(false, "BUG") 38 | _pending_drains -= 1 39 | if _pending_drains == 0: 40 | complete_release(result) 41 | unreference() 42 | else: 43 | result[drain_index] = NesinkronaCompletedAsync.new(drain) 44 | _pending_drains -= 1 45 | if _pending_drains == 0: 46 | complete_release(result) 47 | -------------------------------------------------------------------------------- /addons/godot-nesink/Cancel.gd: -------------------------------------------------------------------------------- 1 | ## [Async] や [AsyncSequence] にキャンセルが要求されたことを伝えるためのクラスです。 2 | class_name Cancel extends RefCounted 3 | 4 | #------------------------------------------------------------------------------- 5 | # シグナル 6 | #------------------------------------------------------------------------------- 7 | 8 | ## [b](アドオン内もしくは実装内でのみ使用)[/b] キャンセルされたとき発火するシグナル。[br] 9 | ## [br] 10 | ## このシグナルはアドオン外からの利用を想定していません。[br] 11 | ## デッドロックしてしまったり、[RefCounted] の寿命を跨いでしまったり、[br] 12 | ## 安全ではないため [Async] の実装内でのみ使用するようにしてください。[br] 13 | ## [br] 14 | ## Usage: 15 | ## [codeblock] 16 | ## var cancel: Cancel = ... 17 | ## if not cancel.is_requested: # 必ずチェックすること 18 | ## await cancel.requested 19 | ## [/codeblock] 20 | signal requested 21 | 22 | #------------------------------------------------------------------------------- 23 | # プロパティ 24 | #------------------------------------------------------------------------------- 25 | 26 | ## キャンセルが要求されているかを返します。 27 | var is_requested: bool: 28 | get: return _is_requested 29 | 30 | #------------------------------------------------------------------------------- 31 | # メソッド 32 | #------------------------------------------------------------------------------- 33 | 34 | ## タイムアウトするとキャンセルが要求されるキャンセルを作成します。 35 | static func timeout(timeout_: float) -> Cancel: 36 | var cancel := new() 37 | NesinkronaAsyncBase \ 38 | .get_tree() \ 39 | .create_timer(timeout_) \ 40 | .timeout \ 41 | .connect(cancel.request) 42 | return cancel 43 | 44 | ## キャンセルされた状態のキャンセルを作成します。 45 | static func canceled() -> Cancel: 46 | if _canceled == null: 47 | _canceled = new() 48 | _canceled.request() 49 | return _canceled 50 | 51 | ## キャンセルを要求します。 52 | func request() -> void: 53 | if not _is_requested: 54 | _is_requested = true 55 | requested.emit() 56 | 57 | #------------------------------------------------------------------------------- 58 | 59 | static var _canceled: Cancel 60 | 61 | var _is_requested := false 62 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | [English](https://github.com/folt-a/godot-nesink/blob/main/README.md) | [日本語](https://github.com/folt-a/godot-nesink/blob/main/README_ja.md) | 简体中文 2 | 3 |
4 | 5 | [![MIT License](https://img.shields.io/badge/License-MIT-25B3A0?style=flat-square)](https://github.com/folt-a/godot-motion/blob/main/LICENSE.md) 6 | 7 |
8 | 9 | # Nesinkrona (for Godot 4) 10 | 11 | 一个增强 GDScript 2.0 await 的插件。 12 | 13 |
14 | 15 | * 它提高了与信号和程序交织在一起的代码的可读性和自然度。(类似于 JS 的 Promise 或 C# 的 Task) 16 | * 由于代码简单,所以速度快。 17 | * 可以从外部取消 await。 18 | * 包含 yield 重现迭代的类型。(实验的) 19 | 20 |
21 | 22 | --- 23 | 24 | ```GDScript 25 | # 多个 Async 或信号 (Signal) 或 Coroutine 会组合成一个新的 Async 26 | var all_async := Async.all([ 27 | Async.from(_coro), # from Async 28 | obj.signal, # from Signal 29 | func(): return 0 # from Coroutine 30 | ]) 31 | var result = await all_async.wait() 32 | 33 | # 或者可以立即 await 它 34 | var result = await Async.wait_all([ 35 | Async.from(_coro), 36 | obj.signal, 37 | func(): return 0 38 | ]) 39 | 40 | # 包括等待的大部分一般模式 41 | Async.all() 42 | Async.all_settled() 43 | Async.any() 44 | Async.race() 45 | Async.wait_all() 46 | Async.wait_all_settled() 47 | Async.wait_any() 48 | Async.wait_race() 49 | 50 | # 支持 Async 的延续和取消 51 | var another_async = async.then(func(prev_result): 52 | return prev_result * prev_result) 53 | var cancel := Cancel.new() 54 | await another_async.wait(cancel) 55 | ``` 56 | 57 |
58 | 59 | --- 60 | 61 |
62 | 63 | ## 如何使用 64 | 65 |
66 | 67 | #### 检查演示项目 68 | 69 | 1. Git Clone 然后在 Godot 引擎中打开。 70 | 2. (选择 `demo/Demo.tscn` 作为主场景然后) F5 71 | 72 | 73 | 74 | #### 安装这插件 75 | 76 | 1. Git clone 然后将 `addons/godot-nesink` 复制到您的项目中。 77 | 2. 启用 `Nesinkrona` 插件。 78 | 3. 写写代码。 79 | 80 |
81 | 82 | 还有细节: [📖 维基 (Google Translated)](https://github-com.translate.goog/folt-a/godot-nesink/wiki/Async?_x_tr_sl=auto&_x_tr_tl=zh-cn) 83 | 84 |
85 | 86 | --- 87 | 88 |
89 | 90 | ## 贡献 91 | 92 | 我们非常欢迎提供 bug 的修复、文档翻译,和任何其他改进! 谢谢! 93 | 94 | ## Original repository 95 | 96 | This repository was transfer from [ydipeepo](https://github.com/ydipeepo) -------------------------------------------------------------------------------- /README_ja.md: -------------------------------------------------------------------------------- 1 | [English](https://github.com/folt-a/godot-nesink/blob/main/README.md) | 日本語 | [简体中文](https://github.com/folt-a/godot-nesink/blob/main/README_zh.md) 2 | 3 |
4 | 5 | [![MIT License](https://img.shields.io/badge/License-MIT-25B3A0?style=flat-square)](https://github.com/folt-a/godot-motion/blob/main/LICENSE.md) 6 | 7 |
8 | 9 | # Nesinkrona (for Godot 4) 10 | 11 | Godot 4 の await 足回りを強化するアドオン。[Async Helper](https://github.com/ydipeepo/godot-async-helper) (Godot 3) の Godot 4 移植版です。 12 | 13 |
14 | 15 | * シグナルやコルーチンが入り組むコードの見通しを良くし、自然に書けるようにします (JS の Promise や C# の Task に近いです) 16 | * 実装が単純なので高速です 17 | * await 外からキャンセルすることができます (外部から中断させることができる) 18 | * yield によるイテレーションを再現するパターンを含みます (実験的) 19 | 20 | --- 21 | 22 |
23 | 24 | ```GDScript 25 | # 複数の Async もしくは Signal, コルーチンを新たな Async としてまとめることができます 26 | var all_async := Async.all([ 27 | Async.from(_coro), # Async から 28 | obj.signal, # Signal から 29 | func(): return 0 # Coroutine から 30 | ]) 31 | var result = await all_async.wait() 32 | 33 | # もしくは、即時待機することもできます 34 | var result = await Async.wait_all([ 35 | Async.from(_coro), # from Async 36 | obj.signal, # from Signal 37 | func(): 38 | # from Coroutine 39 | pass 40 | ]) 41 | 42 | # await のための一般的なメソッドをすべて含みます 43 | Async.all() 44 | Async.all_settled() 45 | Async.any() 46 | Async.race() 47 | Async.wait_all() 48 | Async.wait_all_settled() 49 | Async.wait_any() 50 | Async.wait_race() 51 | 52 | # 継続やキャンセルもサポートします 53 | var another_async = async.then(func(prev_result): 54 | return prev_result * prev_result) 55 | var cancel := Cancel.new() 56 | await another_async.wait(cancel) 57 | ``` 58 | 59 |
60 | 61 | --- 62 | 63 |
64 | 65 | ## 準備 66 | 67 |
68 | 69 | #### デモを確認したい 70 | 71 | 1. このリポジトリをクローンして Godot Engine で開きます 72 | 2. (`demos/Demo.tscn` をメインシーンに設定して、) F5 73 | 74 | #### アドオンを導入したい 75 | 76 | 1. `addons/godot-nesink` ディレクトリを丸ごとプロジェクトに複製します 77 | 2. `Nesinkrona` アドオンを有効にします 78 | 3. 書く。 79 | 80 |
81 | 82 | Async クラス詳細はこちら: [📖 Wiki](https://github.com/folt-a/godot-nesink/wiki/Async) 83 | 84 |
85 | 86 | --- 87 | 88 |
89 | 90 | ## バグの報告や要望など 91 | 92 | バグの修正や報告、ドキュメント翻訳、およびその他の改善など歓迎いたします。 93 | 94 | ## 元リポジトリ 95 | 96 | このリポジトリは、[ydipeepo](https://github.com/ydipeepo) さんから転送されたものです。 97 | 98 | ありがとうございます……! -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaFromSignalAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaFromSignalAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | # 定数 5 | #--------------------------------------------------------------------------------------------------- 6 | 7 | const MAX_SIGNAL_ARGC := 5 8 | 9 | #--------------------------------------------------------------------------------------------------- 10 | 11 | var _object: Object 12 | 13 | func _init(signal_: Signal, signal_argc: int) -> void: 14 | assert(signal_argc <= MAX_SIGNAL_ARGC) 15 | 16 | _object = signal_.get_object() 17 | 18 | if is_instance_valid(_object): 19 | match signal_argc: 20 | 0: signal_.connect(_on_completed_0, CONNECT_REFERENCE_COUNTED) 21 | 1: signal_.connect(_on_completed_1, CONNECT_REFERENCE_COUNTED) 22 | 2: signal_.connect(_on_completed_2, CONNECT_REFERENCE_COUNTED) 23 | 3: signal_.connect(_on_completed_3, CONNECT_REFERENCE_COUNTED) 24 | 4: signal_.connect(_on_completed_4, CONNECT_REFERENCE_COUNTED) 25 | 5: signal_.connect(_on_completed_5, CONNECT_REFERENCE_COUNTED) 26 | else: 27 | assert(not signal_.is_null()) 28 | match signal_argc: 29 | 0: signal_.connect(_on_completed_0) 30 | 1: signal_.connect(_on_completed_1) 31 | 2: signal_.connect(_on_completed_2) 32 | 3: signal_.connect(_on_completed_3) 33 | 4: signal_.connect(_on_completed_4) 34 | 5: signal_.connect(_on_completed_5) 35 | 36 | func _on_completed_0() -> void: 37 | if is_pending: 38 | complete_release([]) 39 | _object = null 40 | 41 | func _on_completed_1(arg1: Variant) -> void: 42 | if is_pending: 43 | complete_release([arg1]) 44 | _object = null 45 | 46 | func _on_completed_2(arg1: Variant, arg2: Variant) -> void: 47 | if is_pending: 48 | complete_release([arg1, arg2]) 49 | _object = null 50 | 51 | func _on_completed_3(arg1: Variant, arg2: Variant, arg3: Variant) -> void: 52 | if is_pending: 53 | complete_release([arg1, arg2, arg3]) 54 | _object = null 55 | 56 | func _on_completed_4(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant) -> void: 57 | if is_pending: 58 | complete_release([arg1, arg2, arg3, arg4]) 59 | _object = null 60 | 61 | func _on_completed_5(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant, arg5: Variant) -> void: 62 | if is_pending: 63 | complete_release([arg1, arg2, arg3, arg4, arg5]) 64 | _object = null 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | English | [日本語](https://github.com/folt-a/godot-nesink/blob/main/README_ja.md) | [简体中文](https://github.com/folt-a/godot-nesink/blob/main/README_zh.md) 2 | 3 |
4 | 5 | [![MIT License](https://img.shields.io/badge/License-MIT-25B3A0?style=flat-square)](https://github.com/folt-a/godot-nesink/blob/main/LICENSE.md) 6 | 7 |
8 | 9 | # Nesinkrona (for Godot 4) 10 | 11 | An addon to enhance the awaitability of GDScript 2.0. 12 | 13 |
14 | 15 | * It improves the readability and naturalness of code that is intermingled with signals and coroutines. (similar to Promise in JS or Task in C#). 16 | * Fast due to simple implementation. 17 | * Can be canceled from outside of await. 18 | * Contains patterns that reproduce iterations with yield. (experimental) 19 | 20 |
21 | 22 | --- 23 | 24 | ```GDScript 25 | # Multiple Async or Signal or Coroutine can be combined into a new Async 26 | var all_async := Async.all([ 27 | Async.from(_coro), # from Async 28 | obj.signal, # from Signal 29 | func(): return 0 # from Coroutine 30 | ]) 31 | var result = await all_async.wait() 32 | 33 | # or you can await it immediately. 34 | var result = await Async.wait_all([ 35 | Async.from(_coro), 36 | obj.signal, 37 | func(): return 0 38 | ]) 39 | 40 | # Includes general patterns for await 41 | Async.all() 42 | Async.all_settled() 43 | Async.any() 44 | Async.race() 45 | Async.wait_all() 46 | Async.wait_all_settled() 47 | Async.wait_any() 48 | Async.wait_race() 49 | 50 | # and support continuations and cancellations. 51 | var another_async = async.then(func(prev_result): 52 | return prev_result * prev_result) 53 | var cancel := Cancel.new() 54 | await another_async.wait(cancel) 55 | ``` 56 | 57 |
58 | 59 | ## How do I use it? 60 | 61 |
62 | 63 | #### Checking demos 64 | 65 | 1. Git clone then open in Godot Engine. 66 | 2. (Select `demo/Demo.tscn` as Main Scene then) F5! 67 | 68 | 69 | 70 | #### Add-on installation 71 | 72 | 1. Git clone then copy `addons/godot-nesink` to your project. 73 | 2. Activate `Nesinkrona` addon. 74 | 3. Code! 75 | 76 |
77 | 78 | And details: [📖 Wiki (Google Translated)](https://github-com.translate.goog/folt-a/godot-nesink/wiki/Async?_x_tr_sl=auto&_x_tr_tl=en) 79 | 80 |
81 | 82 | --- 83 | 84 |
85 | 86 | ## Contributing 87 | 88 | We are grateful to the community for contributing bug fixes, documentation, translations, and any other improvements! 89 | 90 | 91 | 92 | ## Original repository 93 | 94 | This repository was transfer from [ydipeepo](https://github.com/ydipeepo) 95 | 96 | ありがとうございます……! -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaFromSignalNameAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaFromSignalNameAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | # 定数 5 | #--------------------------------------------------------------------------------------------------- 6 | 7 | const MAX_SIGNAL_ARGC := 5 8 | 9 | #--------------------------------------------------------------------------------------------------- 10 | 11 | var _object: Object 12 | var _signal_name: StringName 13 | 14 | func _init(object: Object, signal_name: StringName, signal_argc: int) -> void: 15 | assert(object != null) 16 | assert(signal_argc <= MAX_SIGNAL_ARGC) 17 | 18 | _object = object 19 | _signal_name = signal_name 20 | 21 | if is_instance_valid(_object): 22 | match signal_argc: 23 | 0: _object.connect(_signal_name, _on_completed_0) 24 | 1: _object.connect(_signal_name, _on_completed_1) 25 | 2: _object.connect(_signal_name, _on_completed_2) 26 | 3: _object.connect(_signal_name, _on_completed_3) 27 | 4: _object.connect(_signal_name, _on_completed_4) 28 | 5: _object.connect(_signal_name, _on_completed_5) 29 | else: 30 | cancel_release() 31 | 32 | func _on_completed_0() -> void: 33 | if is_instance_valid(_object): 34 | _object.disconnect(_signal_name, _on_completed_0) 35 | _object = null 36 | if is_pending: 37 | complete_release([]) 38 | 39 | func _on_completed_1(arg1: Variant) -> void: 40 | if is_instance_valid(_object): 41 | _object.disconnect(_signal_name, _on_completed_1) 42 | _object = null 43 | if is_pending: 44 | complete_release([arg1]) 45 | 46 | func _on_completed_2(arg1: Variant, arg2: Variant) -> void: 47 | if is_instance_valid(_object): 48 | _object.disconnect(_signal_name, _on_completed_2) 49 | _object = null 50 | if is_pending: 51 | complete_release([arg1, arg2]) 52 | 53 | func _on_completed_3(arg1: Variant, arg2: Variant, arg3: Variant) -> void: 54 | if is_instance_valid(_object): 55 | _object.disconnect(_signal_name, _on_completed_3) 56 | _object = null 57 | if is_pending: 58 | complete_release([arg1, arg2, arg3]) 59 | 60 | func _on_completed_4(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant) -> void: 61 | if is_instance_valid(_object): 62 | _object.disconnect(_signal_name, _on_completed_4) 63 | _object = null 64 | if is_pending: 65 | complete_release([arg1, arg2, arg3, arg4]) 66 | 67 | func _on_completed_5(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant, arg5: Variant) -> void: 68 | if is_instance_valid(_object): 69 | _object.disconnect(_signal_name, _on_completed_5) 70 | _object = null 71 | if is_pending: 72 | complete_release([arg1, arg2, arg3, arg4, arg5]) 73 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaAsyncBase.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaAsyncBase extends Async 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | # メソッド 5 | #--------------------------------------------------------------------------------------------------- 6 | 7 | static func get_tree() -> SceneTree: 8 | if _tree == null: 9 | _tree = Engine.get_main_loop() 10 | return _tree 11 | 12 | func get_state() -> int: 13 | return _state 14 | 15 | func wait(cancel: Cancel = null): 16 | if _state == STATE_PENDING: 17 | _state = STATE_PENDING_WITH_WAITERS 18 | if _state == STATE_PENDING_WITH_WAITERS: 19 | if cancel: 20 | await _wait_with_cancel(cancel) 21 | else: 22 | await _wait() 23 | return _result 24 | 25 | func complete_release(result: Variant) -> void: 26 | match _state: 27 | STATE_PENDING: 28 | _result = result 29 | _state = STATE_COMPLETED 30 | STATE_PENDING_WITH_WAITERS: 31 | _result = result 32 | _state = STATE_COMPLETED 33 | _release.emit() 34 | 35 | func cancel_release() -> void: 36 | match _state: 37 | STATE_PENDING: 38 | _state = STATE_CANCELED 39 | STATE_PENDING_WITH_WAITERS: 40 | _state = STATE_CANCELED 41 | _release.emit() 42 | 43 | # https://github.com/folt-a/godot-nesink/issues/4 44 | static func normalize_drain(drain): 45 | if drain is Array: 46 | match len(drain): 47 | # 3: 48 | # if drain[0] is Object and (drain[1] is String or drain[1] is StringName) and drain[2] is int: 49 | # return NesinkronaFromSignalNameAsync.new(drain[0], drain[1], drain[2]) 50 | 2: 51 | # if drain[0] is Object and (drain[1] is String or drain[1] is StringName): 52 | # return NesinkronaFromSignalNameAsync.new(drain[0], drain[1], 0) 53 | if drain[0] is Signal and drain[1] is int: 54 | return NesinkronaFromSignalAsync.new(drain[0], drain[1]) 55 | 1: 56 | # if drain[0] is Object: 57 | # return NesinkronaFromSignalNameAsync.new(drain[0], "completed", 0) 58 | if drain[0] is Signal: 59 | return NesinkronaFromSignalAsync.new(drain[0], 0) 60 | 61 | # if drain is Object: 62 | # return NesinkronaFromSignalNameAsync.new(drain[0], "completed", 0) 63 | 64 | if drain is Signal: 65 | return NesinkronaFromSignalAsync.new(drain, 0) 66 | 67 | if drain is Callable: 68 | return NesinkronaFromAsync.new(drain) 69 | 70 | return drain 71 | 72 | #--------------------------------------------------------------------------------------------------- 73 | 74 | signal _release 75 | 76 | static var _tree: SceneTree 77 | 78 | var _state := STATE_PENDING 79 | var _result 80 | 81 | func _wait() -> void: 82 | assert(_state == STATE_PENDING_WITH_WAITERS) 83 | await _release 84 | 85 | func _wait_with_cancel(cancel: Cancel) -> void: 86 | assert(_state == STATE_PENDING_WITH_WAITERS) 87 | 88 | if cancel.is_requested: 89 | cancel_release() 90 | return 91 | 92 | cancel.reference() 93 | cancel.requested.connect(_on_cancel_requested) 94 | await _release 95 | cancel.requested.disconnect(_on_cancel_requested) 96 | cancel.unreference() 97 | 98 | func _on_cancel_requested() -> void: 99 | assert(_state <= STATE_PENDING_WITH_WAITERS) 100 | cancel_release() 101 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaConditionalSignalNameAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaFromConditionalSignalNameAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | # 定数 5 | #--------------------------------------------------------------------------------------------------- 6 | 7 | const MAX_SIGNAL_ARGC := 5 8 | 9 | #--------------------------------------------------------------------------------------------------- 10 | 11 | var _object: Object 12 | var _signal_name: StringName 13 | var _signal_args: Array 14 | 15 | static func _match(a: Variant, b: Variant) -> bool: 16 | return a is Object and a == SKIP or a == b 17 | 18 | func _init(object: Object, signal_name: StringName, signal_args: Array) -> void: 19 | assert(object != null) 20 | assert(signal_args.size() <= MAX_SIGNAL_ARGC) 21 | 22 | _object = object 23 | _signal_name = signal_name 24 | _signal_args = signal_args 25 | 26 | if is_instance_valid(_object): 27 | match len(_signal_args): 28 | 0: _object.connect(_signal_name, _on_completed_0) 29 | 1: _object.connect(_signal_name, _on_completed_1) 30 | 2: _object.connect(_signal_name, _on_completed_2) 31 | 3: _object.connect(_signal_name, _on_completed_3) 32 | 4: _object.connect(_signal_name, _on_completed_4) 33 | 5: _object.connect(_signal_name, _on_completed_5) 34 | else: 35 | cancel_release() 36 | 37 | func _on_completed_0() -> void: 38 | if is_instance_valid(_object): 39 | _object.disconnect(_signal_name, _on_completed_0) 40 | _object = null 41 | if is_pending: 42 | complete_release([]) 43 | 44 | func _on_completed_1(arg1: Variant) -> void: 45 | if (_match(_signal_args[0], arg1)): 46 | if is_instance_valid(_object): 47 | _object.disconnect(_signal_name, _on_completed_1) 48 | _object = null 49 | if is_pending: 50 | complete_release([arg1]) 51 | 52 | func _on_completed_2(arg1: Variant, arg2: Variant) -> void: 53 | if (_match(_signal_args[0], arg1) and 54 | _match(_signal_args[1], arg2)): 55 | if is_instance_valid(_object): 56 | _object.disconnect(_signal_name, _on_completed_2) 57 | _object = null 58 | if is_pending: 59 | complete_release([arg1, arg2]) 60 | 61 | func _on_completed_3(arg1: Variant, arg2: Variant, arg3: Variant) -> void: 62 | if (_match(_signal_args[0], arg1) and 63 | _match(_signal_args[1], arg2) and 64 | _match(_signal_args[2], arg3)): 65 | if is_instance_valid(_object): 66 | _object.disconnect(_signal_name, _on_completed_3) 67 | _object = null 68 | if is_pending: 69 | complete_release([arg1, arg2, arg3]) 70 | 71 | func _on_completed_4(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant) -> void: 72 | if (_match(_signal_args[0], arg1) and 73 | _match(_signal_args[1], arg2) and 74 | _match(_signal_args[2], arg3) and 75 | _match(_signal_args[3], arg4)): 76 | if is_instance_valid(_object): 77 | _object.disconnect(_signal_name, _on_completed_4) 78 | _object = null 79 | if is_pending: 80 | complete_release([arg1, arg2, arg3, arg4]) 81 | 82 | func _on_completed_5(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant, arg5: Variant) -> void: 83 | if (_match(_signal_args[0], arg1) and 84 | _match(_signal_args[1], arg2) and 85 | _match(_signal_args[2], arg3) and 86 | _match(_signal_args[3], arg4) and 87 | _match(_signal_args[4], arg5)): 88 | if is_instance_valid(_object): 89 | _object.disconnect(_signal_name, _on_completed_5) 90 | _object = null 91 | if is_pending: 92 | complete_release([arg1, arg2, arg3, arg4, arg5]) 93 | -------------------------------------------------------------------------------- /addons/godot-nesink/NesinkronaConditionalSignalAsync.gd: -------------------------------------------------------------------------------- 1 | class_name NesinkronaFromConditionalSignalAsync extends NesinkronaAsyncBase 2 | 3 | #--------------------------------------------------------------------------------------------------- 4 | # 定数 5 | #--------------------------------------------------------------------------------------------------- 6 | 7 | const MAX_SIGNAL_ARGC := 5 8 | 9 | #--------------------------------------------------------------------------------------------------- 10 | 11 | var _object: Object 12 | var _signal: Signal 13 | var _signal_args: Array 14 | 15 | static func _match(a: Variant, b: Variant) -> bool: 16 | return a is Object and a == SKIP or a == b 17 | 18 | func _init(signal_: Signal, signal_args: Array) -> void: 19 | assert(signal_args.size() <= MAX_SIGNAL_ARGC) 20 | 21 | _object = signal_.get_object() 22 | _signal = signal_ 23 | _signal_args = signal_args 24 | 25 | assert(is_instance_valid(_object) or not signal_.is_null()) 26 | 27 | match len(_signal_args): 28 | 0: signal_.connect(_on_completed_0) 29 | 1: signal_.connect(_on_completed_1) 30 | 2: signal_.connect(_on_completed_2) 31 | 3: signal_.connect(_on_completed_3) 32 | 4: signal_.connect(_on_completed_4) 33 | 5: signal_.connect(_on_completed_5) 34 | 35 | func _on_completed_0() -> void: 36 | if is_pending: 37 | if is_instance_valid(_object) and _signal.is_connected(_on_completed_0): 38 | _signal.disconnect(_on_completed_0) 39 | complete_release([]) 40 | _object = null 41 | 42 | func _on_completed_1(arg1: Variant) -> void: 43 | if is_pending: 44 | if (not _match(_signal_args[0], arg1)): 45 | return 46 | if is_instance_valid(_object) and _signal.is_connected(_on_completed_1): 47 | _signal.disconnect(_on_completed_1) 48 | complete_release([arg1]) 49 | _object = null 50 | 51 | func _on_completed_2(arg1: Variant, arg2: Variant) -> void: 52 | if is_pending: 53 | if (not _match(_signal_args[0], arg1) or 54 | not _match(_signal_args[1], arg2)): 55 | return 56 | if is_instance_valid(_object) and _signal.is_connected(_on_completed_2): 57 | _signal.disconnect(_on_completed_2) 58 | complete_release([arg1, arg2]) 59 | _object = null 60 | 61 | func _on_completed_3(arg1: Variant, arg2: Variant, arg3: Variant) -> void: 62 | if is_pending: 63 | if (not _match(_signal_args[0], arg1) or 64 | not _match(_signal_args[1], arg2) or 65 | not _match(_signal_args[2], arg3)): 66 | return 67 | if is_instance_valid(_object) and _signal.is_connected(_on_completed_3): 68 | _signal.disconnect(_on_completed_3) 69 | complete_release([arg1, arg2, arg3]) 70 | _object = null 71 | 72 | func _on_completed_4(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant) -> void: 73 | if is_pending: 74 | if (not _match(_signal_args[0], arg1) or 75 | not _match(_signal_args[1], arg2) or 76 | not _match(_signal_args[2], arg3) or 77 | not _match(_signal_args[3], arg4)): 78 | return 79 | if is_instance_valid(_object) and _signal.is_connected(_on_completed_4): 80 | _signal.disconnect(_on_completed_4) 81 | complete_release([arg1, arg2, arg3, arg4]) 82 | _object = null 83 | 84 | func _on_completed_5(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant, arg5: Variant) -> void: 85 | if is_pending: 86 | if (not _match(_signal_args[0], arg1) or 87 | not _match(_signal_args[1], arg2) or 88 | not _match(_signal_args[2], arg3) or 89 | not _match(_signal_args[3], arg4) or 90 | not _match(_signal_args[4], arg5)): 91 | return 92 | if is_instance_valid(_object) and _signal.is_connected(_on_completed_5): 93 | _signal.disconnect(_on_completed_5) 94 | complete_release([arg1, arg2, arg3, arg4, arg5]) 95 | _object = null 96 | -------------------------------------------------------------------------------- /Test_Task.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://63dkar7hk0f8"] 2 | 3 | [ext_resource type="Script" path="res://Test_Task.gd" id="1_6j14v"] 4 | 5 | [node name="Test_Async" type="Control"] 6 | layout_mode = 3 7 | anchors_preset = 15 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | grow_horizontal = 2 11 | grow_vertical = 2 12 | script = ExtResource("1_6j14v") 13 | 14 | [node name="Title" type="Label" parent="."] 15 | layout_mode = 0 16 | offset_left = 64.0 17 | offset_top = 48.0 18 | offset_right = 576.0 19 | offset_bottom = 96.0 20 | theme_override_font_sizes/font_size = 24 21 | text = "Nesinkrona" 22 | vertical_alignment = 1 23 | metadata/_edit_lock_ = true 24 | 25 | [node name="Description" type="Label" parent="."] 26 | layout_mode = 0 27 | offset_left = 64.0 28 | offset_top = 96.0 29 | offset_right = 576.0 30 | offset_bottom = 128.0 31 | theme_override_colors/font_color = Color(0.733333, 0.733333, 0.733333, 1) 32 | theme_override_font_sizes/font_size = 12 33 | text = "Pseudo (task based) asynchronous patterns for Godot 4." 34 | vertical_alignment = 1 35 | metadata/_edit_lock_ = true 36 | 37 | [node name="Features" type="HBoxContainer" parent="."] 38 | auto_translate_mode = 2 39 | layout_mode = 0 40 | offset_left = 64.0 41 | offset_top = 192.0 42 | offset_right = 1216.0 43 | offset_bottom = 576.0 44 | localize_numeral_system = false 45 | theme_override_constants/separation = 0 46 | metadata/_edit_group_ = true 47 | 48 | [node name="Version_1_0_1" type="VBoxContainer" parent="Features"] 49 | custom_minimum_size = Vector2(256, 0) 50 | layout_mode = 2 51 | theme_override_constants/separation = 0 52 | metadata/_edit_group_ = true 53 | 54 | [node name="Header" type="Label" parent="Features/Version_1_0_1"] 55 | custom_minimum_size = Vector2(0, 32) 56 | layout_mode = 2 57 | theme_override_colors/font_color = Color(1, 1, 1, 1) 58 | text = "1.0.1 features:" 59 | vertical_alignment = 1 60 | 61 | [node name="Completed" type="CheckBox" parent="Features/Version_1_0_1"] 62 | unique_name_in_owner = true 63 | custom_minimum_size = Vector2(0, 24) 64 | layout_mode = 2 65 | focus_mode = 0 66 | mouse_filter = 2 67 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 68 | theme_override_font_sizes/font_size = 10 69 | disabled = true 70 | button_mask = 0 71 | text = "Async.completed()" 72 | 73 | [node name="Canceled" type="CheckBox" parent="Features/Version_1_0_1"] 74 | unique_name_in_owner = true 75 | custom_minimum_size = Vector2(0, 24) 76 | layout_mode = 2 77 | focus_mode = 0 78 | mouse_filter = 2 79 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 80 | theme_override_font_sizes/font_size = 10 81 | disabled = true 82 | button_mask = 0 83 | text = "Async.canceled()" 84 | 85 | [node name="From" type="CheckBox" parent="Features/Version_1_0_1"] 86 | unique_name_in_owner = true 87 | custom_minimum_size = Vector2(0, 24) 88 | layout_mode = 2 89 | focus_mode = 0 90 | mouse_filter = 2 91 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 92 | theme_override_font_sizes/font_size = 10 93 | disabled = true 94 | button_mask = 0 95 | text = "Async.from()" 96 | 97 | [node name="FromSignal" type="CheckBox" parent="Features/Version_1_0_1"] 98 | unique_name_in_owner = true 99 | custom_minimum_size = Vector2(0, 24) 100 | layout_mode = 2 101 | focus_mode = 0 102 | mouse_filter = 2 103 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 104 | theme_override_font_sizes/font_size = 10 105 | disabled = true 106 | button_mask = 0 107 | text = "Async.from_signal()" 108 | 109 | [node name="FromSignalName" type="CheckBox" parent="Features/Version_1_0_1"] 110 | unique_name_in_owner = true 111 | custom_minimum_size = Vector2(0, 24) 112 | layout_mode = 2 113 | focus_mode = 0 114 | mouse_filter = 2 115 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 116 | theme_override_font_sizes/font_size = 10 117 | disabled = true 118 | button_mask = 0 119 | text = "Async.from_signal_name()" 120 | 121 | [node name="Delay" type="CheckBox" parent="Features/Version_1_0_1"] 122 | unique_name_in_owner = true 123 | custom_minimum_size = Vector2(0, 24) 124 | layout_mode = 2 125 | focus_mode = 0 126 | mouse_filter = 2 127 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 128 | theme_override_font_sizes/font_size = 10 129 | disabled = true 130 | button_mask = 0 131 | text = "Async.delay()" 132 | 133 | [node name="DelayMsec" type="CheckBox" parent="Features/Version_1_0_1"] 134 | unique_name_in_owner = true 135 | custom_minimum_size = Vector2(0, 24) 136 | layout_mode = 2 137 | focus_mode = 0 138 | mouse_filter = 2 139 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 140 | theme_override_font_sizes/font_size = 10 141 | disabled = true 142 | button_mask = 0 143 | text = "Async.delay_msec()" 144 | 145 | [node name="DelayUsec" type="CheckBox" parent="Features/Version_1_0_1"] 146 | unique_name_in_owner = true 147 | custom_minimum_size = Vector2(0, 24) 148 | layout_mode = 2 149 | focus_mode = 0 150 | mouse_filter = 2 151 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 152 | theme_override_font_sizes/font_size = 10 153 | disabled = true 154 | button_mask = 0 155 | text = "Async.delay_usec()" 156 | 157 | [node name="All" type="CheckBox" parent="Features/Version_1_0_1"] 158 | unique_name_in_owner = true 159 | custom_minimum_size = Vector2(0, 24) 160 | layout_mode = 2 161 | focus_mode = 0 162 | mouse_filter = 2 163 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 164 | theme_override_font_sizes/font_size = 10 165 | disabled = true 166 | button_mask = 0 167 | text = "Async.all()" 168 | 169 | [node name="AllSettled" type="CheckBox" parent="Features/Version_1_0_1"] 170 | unique_name_in_owner = true 171 | custom_minimum_size = Vector2(0, 24) 172 | layout_mode = 2 173 | focus_mode = 0 174 | mouse_filter = 2 175 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 176 | theme_override_font_sizes/font_size = 10 177 | disabled = true 178 | button_mask = 0 179 | text = "Async.all_settled()" 180 | 181 | [node name="Any" type="CheckBox" parent="Features/Version_1_0_1"] 182 | unique_name_in_owner = true 183 | custom_minimum_size = Vector2(0, 24) 184 | layout_mode = 2 185 | focus_mode = 0 186 | mouse_filter = 2 187 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 188 | theme_override_font_sizes/font_size = 10 189 | disabled = true 190 | button_mask = 0 191 | text = "Async.any()" 192 | 193 | [node name="Race" type="CheckBox" parent="Features/Version_1_0_1"] 194 | unique_name_in_owner = true 195 | custom_minimum_size = Vector2(0, 24) 196 | layout_mode = 2 197 | focus_mode = 0 198 | mouse_filter = 2 199 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 200 | theme_override_font_sizes/font_size = 10 201 | disabled = true 202 | button_mask = 0 203 | text = "Async.race()" 204 | 205 | [node name="Then" type="CheckBox" parent="Features/Version_1_0_1"] 206 | unique_name_in_owner = true 207 | custom_minimum_size = Vector2(0, 24) 208 | layout_mode = 2 209 | focus_mode = 0 210 | mouse_filter = 2 211 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 212 | theme_override_font_sizes/font_size = 10 213 | disabled = true 214 | button_mask = 0 215 | text = "Async.then()" 216 | 217 | [node name="Unwrap" type="CheckBox" parent="Features/Version_1_0_1"] 218 | unique_name_in_owner = true 219 | custom_minimum_size = Vector2(0, 24) 220 | layout_mode = 2 221 | focus_mode = 0 222 | mouse_filter = 2 223 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 224 | theme_override_font_sizes/font_size = 10 225 | disabled = true 226 | button_mask = 0 227 | text = "Async.unwrap()" 228 | 229 | [node name="Version_1_0_2" type="VBoxContainer" parent="Features"] 230 | custom_minimum_size = Vector2(256, 0) 231 | layout_mode = 2 232 | theme_override_constants/separation = 0 233 | metadata/_edit_group_ = true 234 | 235 | [node name="Header" type="Label" parent="Features/Version_1_0_2"] 236 | custom_minimum_size = Vector2(0, 32) 237 | layout_mode = 2 238 | theme_override_colors/font_color = Color(1, 1, 1, 1) 239 | text = "1.0.2 features:" 240 | vertical_alignment = 1 241 | 242 | [node name="Defer" type="CheckBox" parent="Features/Version_1_0_2"] 243 | unique_name_in_owner = true 244 | custom_minimum_size = Vector2(0, 24) 245 | layout_mode = 2 246 | focus_mode = 0 247 | mouse_filter = 2 248 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 249 | theme_override_font_sizes/font_size = 10 250 | disabled = true 251 | button_mask = 0 252 | text = "Async.defer()" 253 | 254 | [node name="FromConditionalSignal" type="CheckBox" parent="Features/Version_1_0_2"] 255 | unique_name_in_owner = true 256 | custom_minimum_size = Vector2(0, 24) 257 | layout_mode = 2 258 | focus_mode = 0 259 | mouse_filter = 2 260 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 261 | theme_override_font_sizes/font_size = 10 262 | disabled = true 263 | button_mask = 0 264 | text = "Async.from_conditional()" 265 | 266 | [node name="FromConditionalSignalName" type="CheckBox" parent="Features/Version_1_0_2"] 267 | unique_name_in_owner = true 268 | custom_minimum_size = Vector2(0, 24) 269 | layout_mode = 2 270 | focus_mode = 0 271 | mouse_filter = 2 272 | theme_override_colors/font_disabled_color = Color(0.8, 0.8, 0.8, 1) 273 | theme_override_font_sizes/font_size = 10 274 | disabled = true 275 | button_mask = 0 276 | text = "Async.from_conditional_signal_name()" 277 | -------------------------------------------------------------------------------- /addons/godot-nesink/Async.gd: -------------------------------------------------------------------------------- 1 | ## 非同期的な処理の完了とその結果、もしくはキャンセルされたという状態を表すインターフェイスです。 2 | ## 3 | ## 非同期的な処理を抽象化し、将来決まる結果をこの共通のインターフェイスからアクセスできるようにします。 4 | ## 完了もしくはキャンセルされたという状態と、完了した場合はその結果を保持し [method wait] メソッドから取得することができます。 5 | ## またいくつかの重要なファクトリメソッドを公開します。 6 | class_name Async 7 | 8 | #------------------------------------------------------------------------------- 9 | # 定数 10 | #------------------------------------------------------------------------------- 11 | 12 | enum { 13 | ## [b](アドオン内もしくは実装内でのみ使用)[/b] 14 | ## まだ完了もしくはキャンセルされておらず待機中であることを表します。 15 | STATE_PENDING, 16 | ## [b](アドオン内もしくは実装内でのみ使用)[/b] 17 | ## まだ完了もしくはキャンセルされておらず待機中であり、 18 | ## 1 度以上 [method wait] メソッドにより待機している箇所があることを表します。 19 | STATE_PENDING_WITH_WAITERS, 20 | ## [b](アドオン内もしくは実装内でのみ使用)[/b] 21 | ## 完了したことを表します。 22 | STATE_COMPLETED, 23 | ## [b](アドオン内もしくは実装内でのみ使用)[/b] 24 | ## キャンセルされたことを表します。 25 | STATE_CANCELED, 26 | } 27 | 28 | enum { 29 | ## アイドル時に処理されるよう遅延されることを表します。 30 | DEFER_IDLE, 31 | ## _process フレームに処理されるよう遅延されることを表します。 32 | DEFER_PROCESS_FRAME, 33 | ## _physics_process フレームに処理されるよう遅延されることを表します。 34 | DEFER_PHYSICS_FRAME, 35 | } 36 | 37 | ## 条件一致を省略するためのプレースホルダ。 38 | static var SKIP := Object.new() 39 | 40 | #------------------------------------------------------------------------------- 41 | # プロパティ 42 | #------------------------------------------------------------------------------- 43 | 44 | ## この待機可能な単位が完了している場合 true を返します。[br] 45 | ## [br] 46 | ## [b]補足[/b][br] 47 | ## [method wait] を待機した後は、[member is_canceled] もしくは 48 | ## このプロパティのうちどちらかが必ず true になります。 49 | var is_completed: bool: 50 | get: 51 | return get_state() == STATE_COMPLETED 52 | 53 | ## この待機可能な単位がキャンセルされている場合 true を返します。[br] 54 | ## [br] 55 | ## [b]補足[/b][br] 56 | ## [method wait] を待機した後は、[member is_completed] もしくは 57 | ## このプロパティのうちどちらかが必ず true になります。 58 | var is_canceled: bool: 59 | get: 60 | return get_state() == STATE_CANCELED 61 | 62 | ## この待機可能な単位がまだ完了もしくはキャンセルされていない場合 true を返します。 63 | var is_pending: bool: 64 | get: 65 | var state := get_state() 66 | return \ 67 | state == STATE_PENDING or \ 68 | state == STATE_PENDING_WITH_WAITERS 69 | 70 | #------------------------------------------------------------------------------- 71 | # メソッド 72 | #------------------------------------------------------------------------------- 73 | 74 | ## 完了した状態の [Async] を作成します。[br] 75 | ## [br] 76 | ## [b]使い方[/b] 77 | ## [codeblock] 78 | ## var async := Async.completed("result") 79 | ## assert(async.is_completed) 80 | ## assert(await async.wait() == "result") 81 | ## [/codeblock] 82 | static func completed(result: Variant = null) -> Async: 83 | return NesinkronaCompletedAsync.new(result) 84 | 85 | ## キャンセルされた状態の [Async] を作成します。[br] 86 | ## [br] 87 | ## [b]使い方[/b] 88 | ## [codeblock] 89 | ## var async := Async.canceled() 90 | ## assert(async.is_canceled) 91 | ## assert(await async.wait() == null) 92 | ## [/codeblock] 93 | static func canceled() -> Async: 94 | return NesinkronaCanceledAsync.new() 95 | 96 | ## コルーチン (もしくは関数) の戻り値を結果として完了する [Async] を作成します。[br] 97 | ## [br] 98 | ## [b]補足[/b][br] 99 | ## 第 1 引数 [code]coroutine[/code] は引数を受け取りません。[br] 100 | ## [br] 101 | ## [b]使い方[/b] 102 | ## [codeblock] 103 | ## var async1 := Async.from(func(): 104 | ## await get_tree().create_timer(1.0).timeout) 105 | ## var async2 := Async.from(func(): 106 | ## await get_tree().create_timer(1.0).timeout 107 | ## return 123) 108 | ## print(await async1.wait()) # 109 | ## print(await async2.wait()) # 123 110 | ## [/codeblock] 111 | static func from(coroutine: Callable) -> Async: 112 | return NesinkronaFromAsync.new(coroutine) 113 | 114 | ## コルーチン (もしくは関数) を呼び出し結果を待機する新たな [Async] を作成します。[br] 115 | ## [br] 116 | ## [b]補足[/b][br] 117 | ## 第 1 引数 [code]coroutine[/code] は引数を 2 つ受け取る 118 | ## [Callable] でなくてはなりません。この引数には完了コールバック、 119 | ## キャンセルコールバックが渡されます。[br] 120 | ## [br] 121 | ## [b]使い方[/b] 122 | ## [codeblock] 123 | ## var async1 := Async.from(func(complete: Callback, cancel: Callback): 124 | ## get_tree().create_timer(1.0).timeout.connect(complete)) 125 | ## var async2 := Async.from(func(): 126 | ## get_tree().create_timer(1.0).timeout.connect(complete.bind(123))) 127 | ## print(await async1.wait()) # 128 | ## print(await async2.wait()) # 123 129 | ## [/codeblock] 130 | static func from_callback(coroutine: Callable, unbind_cancel := false) -> Async: 131 | return NesinkronaFromCallbackAsync.new(coroutine, unbind_cancel) 132 | 133 | ## シグナルを受信するとその引数を結果として完了する [Async] を作成します。[br] 134 | ## [br] 135 | ## シグナル引数は [Array] に格納され結果となります。[br] 136 | ## [br] 137 | ## [b]補足[/b][br] 138 | ## もしシグナルが引数を持つ場合、その個数を [code]signal_argc[/code] に指定する必要があります。[br] 139 | ## 間違った個数を指定すると正しく受信できません。[br] 140 | ## [br] 141 | ## [b]使い方[/b] 142 | ## [codeblock] 143 | ## signal my_event_0 144 | ## signal my_event_1(a) 145 | ## signal my_event_2(a, b) 146 | ## 147 | ## var async0 := Async.from_signal(my_event_0) 148 | ## var async1 := Async.from_signal(my_event_1, 1) # シグナルの引数の個数を指定する必要があります 149 | ## var async2 := Async.from_signal(my_event_2, 2) # 同上 150 | ## print(await async0.wait()) # [] 151 | ## print(await async1.wait()) # [1] 152 | ## print(await async2.wait()) # [1, 2] 153 | ## 154 | ## my_event_0.emit() # どこかにこのようなエミッションがあるとします 155 | ## my_event_1.emit(1) # 同上 156 | ## my_event_2.emit(1, 2) # 同上 157 | ## [/codeblock] 158 | static func from_signal(signal_: Signal, signal_argc := 0) -> Async: 159 | assert(not signal_.is_null()) 160 | assert( 161 | signal_argc >= 0 and 162 | signal_argc <= NesinkronaFromSignalAsync.MAX_SIGNAL_ARGC) 163 | return NesinkronaFromSignalAsync.new(signal_, signal_argc) 164 | 165 | ## 文字列によりシグナル名を指定し、そのシグナルを受信すると引数を結果として完了する [Async] を作成します。[br] 166 | ## [br] 167 | ## [b]補足[/b][br] 168 | ## シグナルの指定方法が異なるだけで、他は [method from_signal] メソッドと同じ注意を払う必要があります。 169 | static func from_signal_name( 170 | object: Object, 171 | signal_name: StringName, 172 | signal_argc := 0) -> Async: 173 | 174 | assert( 175 | object != null and 176 | not object.is_queued_for_deletion()) 177 | assert( 178 | signal_argc >= 0 and 179 | signal_argc <= NesinkronaFromSignalNameAsync.MAX_SIGNAL_ARGC) 180 | return NesinkronaFromSignalNameAsync.new(object, signal_name, signal_argc) 181 | 182 | ## シグナル引数が一致した場合にのみ完了する条件付き [Async] を作成します。[br] 183 | ## [br] 184 | ## [method Async.wait] はシグナル引数を配列に格納したものを返します。 185 | static func from_conditional_signal( 186 | signal_: Signal, 187 | signal_args := []) -> Async: 188 | 189 | assert(not signal_.is_null()) 190 | assert( 191 | signal_args.size() <= NesinkronaFromConditionalSignalAsync.MAX_SIGNAL_ARGC) 192 | return NesinkronaFromConditionalSignalAsync.new(signal_, signal_args) 193 | 194 | ## シグナル引数が一致した場合にのみ完了する条件付き [Async] を作成します。[br] 195 | ## [br] 196 | ## [method Async.wait] はシグナル引数を配列に格納したものを返します。 197 | static func from_conditional_signal_name( 198 | object: Object, 199 | signal_name: StringName, 200 | signal_args := []) -> Async: 201 | 202 | assert(object and not object.is_queued_for_deletion()) 203 | assert( 204 | signal_args.size() <= NesinkronaFromConditionalSignalNameAsync.MAX_SIGNAL_ARGC) 205 | return NesinkronaFromConditionalSignalNameAsync.new(object, signal_name, signal_args) 206 | 207 | ## [param callsite] で指定したタイミングで完了する [Async] を作成します。 208 | static func defer(callsite := DEFER_IDLE) -> Async: 209 | match callsite: 210 | DEFER_IDLE: 211 | return NesinkronaDeferIdleAsync.new() 212 | DEFER_PROCESS_FRAME: 213 | return NesinkronaDeferProcessFrameAsync.new() 214 | DEFER_PHYSICS_FRAME: 215 | return NesinkronaDeferPhysicsFrameAsync.new() 216 | _: 217 | assert(false) 218 | return null 219 | 220 | ## タイムアウトすると完了する [Async] を作成します。[br] 221 | ## [br] 222 | ## 結果は第 1 引数 [code]timeout[/code] と同じ値となります。[br] 223 | ## [br] 224 | ## [b]補足[/b][br] 225 | ## 第 1 引数 [code]timeout[/code] は 0.0 以上である必要があります。 226 | ## また過去は指定できません。[br] 227 | ## [br] 228 | ## [b]使い方[/b] 229 | ## [codeblock] 230 | ## var async := Async.delay(1.0) 231 | ## print(await async.wait()) # 1.0 232 | ## [/codeblock] 233 | static func delay(timeout: float) -> Async: 234 | assert(NesinkronaDelayAsync.MIN_TIMEOUT <= timeout) 235 | return \ 236 | completed(0.0) \ 237 | if timeout == 0.0 else \ 238 | NesinkronaDelayAsync.new(timeout) 239 | 240 | ## タイムアウト (ミリ秒) すると完了する [Async] を返します。[br] 241 | ## [br] 242 | ## [b]補足[/b][br] 243 | ## タイムアウトの単位が異なるだけで、他は [method delay] メソッドと同じ注意を払う必要があります。 244 | static func delay_msec(timeout: float) -> Async: 245 | return delay(timeout / 1_000.0) 246 | 247 | ## タイムアウト (マイクロ秒) すると完了する [Async] を返します。[br] 248 | ## [br] 249 | ## [b]補足[/b][br] 250 | ## タイムアウトの単位が異なるだけで、他は [method delay] メソッドと同じ注意を払う必要があります。 251 | static func delay_usec(timeout: float) -> Async: 252 | return delay(timeout / 1_000_000.0) 253 | 254 | ## すべての入力が完了すると、それぞれの結果を配列に格納したものを結果として完了する新たな [Async] を作成します。[br] 255 | ## [br] 256 | ## 第 1 引数 [code]drains[/code] 配列には [Async] や [AsyncSequence] 以外の値も含めることができます。[br] 257 | ## [br] 258 | ## [b]補足[/b][br] 259 | ## 第 1 引数 [code]drains[/code] 配列に [Async] や [AsyncSequence] が含まれている場合、 260 | ## どれか 1 つでもキャンセルされるとこのメソッドで作成した [Async] はキャンセルされた状態となります。 261 | ## これは結果を作成できないためです。もし完了やキャンセルを問わず待機が終わったことだけを待つ場合、 262 | ## このメソッドではなく代わりに [method all_settled] メソッドを使います。[br] 263 | ## [br] 264 | ## [b]使い方[/b] 265 | ## [codeblock] 266 | ## var async := Async.all([ 267 | ## Async.completed(1234), 268 | ## Async.delay(1.5), 269 | ## Async.delay(2.0), 270 | ## true, 271 | ## "aaa", 272 | ## ) 273 | ## print(await async.wait()) # [1234, 1.5, 2.0, true, "aaa"] 274 | ## [/codeblock] 275 | static func all(drains: Array, cancel: Cancel = null) -> Async: 276 | return \ 277 | canceled() \ 278 | if cancel and cancel.is_requested else \ 279 | completed([]) \ 280 | if drains.is_empty() else \ 281 | NesinkronaAllAsync.new(drains, cancel) 282 | 283 | ## すべての入力が完了もしくはキャンセルされると、それぞれの結果を [Async] のまま配列に格納したものを結果として完了する [Async] を作成します。[br] 284 | ## [br] 285 | ## 第 1 引数 [code]drains[/code] 配列には [Async] や [AsyncSequence] 以外の値も含めることができます。[br] 286 | ## [br] 287 | ## [b]補足[/b][br] 288 | ## 第 1 引数 [code]drains[/code] 配列に [Async] や [AsyncSequence] ではない値が含まれていると、 289 | ## それは完了した状態の [Async] に変換され処理されます。[br] 290 | ## [br] 291 | ## [b]使い方[/b] 292 | ## [codeblock] 293 | ## var async := Async.all_settled([ 294 | ## Async.delay(1.0), 295 | ## Async.delay(2.0), 296 | ## 10, 297 | ## 20, 298 | ## ]) 299 | ## var async_result: Array[Async] = await async.wait() 300 | ## print(await async_result[0].wait()) # 1.0 301 | ## print(await async_result[1].wait()) # 2.0 302 | ## print(await async_result[2].wait()) # 10 303 | ## print(await async_result[3].wait()) # 20 304 | ## [/codeblock] 305 | static func all_settled(drains: Array, cancel: Cancel = null) -> Async: 306 | return \ 307 | completed([]) \ 308 | if drains.is_empty() else \ 309 | NesinkronaAllSettledAsync.new(drains, cancel) 310 | 311 | ## すべての入力のうちどれか 1 つが完了すると、それを結果として完了する新たな [Async] を作成します。[br] 312 | ## [br] 313 | ## 第 1 引数 [code]drains[/code] 配列には [Async] や [AsyncSequence] 以外の値も含めることができます。 314 | ## [br] 315 | # [b]補足[/b][br] 316 | ## もし完了やキャンセルを問わずど待機が終わったことだけを待つ場合、 317 | ## このメソッドではなく代わりに [method race] メソッドを使います。[br] 318 | ## [br] 319 | ## [b]使い方[/b] 320 | ## [codeblock] 321 | ## var async := Async.any([ 322 | ## Async.canceled(), 323 | ## Async.delay(2.0), 324 | ## Async.delay(3.0), 325 | ## ]) 326 | ## print(await async.wait()) # 2.0 327 | ## [/codeblock] 328 | static func any(drains: Array, cancel: Cancel = null) -> Async: 329 | return \ 330 | canceled() \ 331 | if cancel != null and cancel.is_requested or drains.is_empty() else \ 332 | NesinkronaAnyAsync.new(drains, cancel) 333 | 334 | ## すべての入力のうちどれか 1 つが完了もしくはキャンセルされると、それを結果として完了する新たな [Async] を作成します。[br] 335 | ## [br] 336 | ## 第 1 引数 [code]drains[/code] 配列には [Async] や [AsyncSequence] 以外の値も含めることができます。[br] 337 | ## [br] 338 | ## [b]補足[/b][br] 339 | ## 第 1 引数 [code]drains[/code] は、1 以上の長さでなくてはなりません。[br] 340 | ## [br] 341 | ## [b]使い方[/b] 342 | ## [codeblock] 343 | ## var async := Async.race([ 344 | ## Async.canceled(), 345 | ## Async.delay(2.0), 346 | ## Async.delay(3.0), 347 | ## ]) 348 | ## async = await async.wait() 349 | ## print(async.is_canceled) # true 350 | ## [/codeblock] 351 | static func race(drains: Array, cancel: Cancel = null) -> Async: 352 | assert(not drains.is_empty()) 353 | return NesinkronaRaceAsync.new(drains, cancel) 354 | 355 | ## [param callsite] で指定したタイミングを待機します。[br] 356 | ## [br] 357 | ## [b]補足[/b][br] 358 | ## 以下と同じです: 359 | ## [codeblock] 360 | ## defer(callsite).wait(cancel) 361 | ## [/codeblock] 362 | static func wait_defer(callsite := DEFER_IDLE, cancel: Cancel = null) -> Variant: 363 | return await defer(callsite).wait(cancel) 364 | 365 | ## [method delay] で作成した [Async] に対して [method wait] を呼び出す短縮表記です。[br] 366 | ## [br] 367 | ## [b]補足[/b][br] 368 | ## 以下と同じです: 369 | ## [codeblock] 370 | ## delay(timeout).wait(cancel) 371 | ## [/codeblock] 372 | static func wait_delay(timeout: float, cancel: Cancel = null) -> Variant: 373 | return await delay(timeout).wait(cancel) 374 | 375 | ## [method delay_msec] で作成した [Async] に対して [method wait] を呼び出す短縮表記です。[br] 376 | ## [br] 377 | ## [b]補足[/b][br] 378 | ## 以下と同じです: 379 | ## [codeblock] 380 | ## delay_msec(timeout).wait(cancel) 381 | ## [/codeblock] 382 | static func wait_delay_msec(timeout: float, cancel: Cancel = null) -> Variant: 383 | return await delay_msec(timeout).wait(cancel) 384 | 385 | ## [method delay_usec] で作成した [Async] に対して [method wait] を呼び出す短縮表記です。[br] 386 | ## [br] 387 | ## [b]補足[/b][br] 388 | ## 以下と同じです: 389 | ## [codeblock] 390 | ## delay_usec(timeout).wait(cancel) 391 | ## [/codeblock] 392 | static func wait_delay_usec(timeout: float, cancel: Cancel = null) -> Variant: 393 | return await delay_usec(timeout).wait(cancel) 394 | 395 | ## [method all] で作成した [Async] に対して [method wait] を呼び出す短縮表記です。[br] 396 | ## [br] 397 | ## [b]補足[/b][br] 398 | ## 以下と同じです: 399 | ## [codeblock] 400 | ## all(drains, cancel).wait(cancel) 401 | ## [/codeblock] 402 | static func wait_all(drains: Array, cancel: Cancel = null) -> Variant: 403 | return await all(drains, cancel).wait(cancel) 404 | 405 | ## [method all_settled] で作成した [Async] に対して [method wait] を呼び出す短縮表記です。[br] 406 | ## [br] 407 | ## [b]補足[/b][br] 408 | ## 以下と同じです: 409 | ## [codeblock] 410 | ## all_settled(drains, cancel).wait(cancel) 411 | ## [/codeblock] 412 | static func wait_all_settled(drains: Array, cancel: Cancel = null) -> Variant: 413 | return await all_settled(drains, cancel).wait(cancel) 414 | 415 | ## [method any] で作成した [Async] に対して [method wait] を呼び出す短縮表記です。[br] 416 | ## [br] 417 | ## [b]補足[/b][br] 418 | ## 以下と同じです: 419 | ## [codeblock] 420 | ## any(drains, cancel).wait(cancel) 421 | ## [/codeblock] 422 | static func wait_any(drains: Array, cancel: Cancel = null) -> Variant: 423 | return await any(drains, cancel).wait(cancel) 424 | 425 | ## [method race] で作成した [Async] に対して [method wait] を呼び出す短縮表記です。[br] 426 | ## [br] 427 | ## [b]補足[/b][br] 428 | ## 以下と同じです: 429 | ## [codeblock] 430 | ## race(drains, cancel).wait(cancel) 431 | ## [/codeblock] 432 | static func wait_race(drains: Array, cancel: Cancel = null) -> Variant: 433 | return await race(drains, cancel).wait(cancel) 434 | 435 | ## この [Async] の結果が [Async] である場合、 436 | ## 指定した回数アンラップを試みた新たな [Async] を作成します。[br] 437 | ## [br] 438 | ## [b]使い方[/b] 439 | ## [codeblock] 440 | ## var async := Async.completed(Async.completed(10)) # Async に Async がネストされている 441 | ## print(await async.unwrap().wait()) # 10 442 | ## [/codeblock] 443 | func unwrap(depth := 1, cancel: Cancel = null) -> Async: 444 | assert(0 <= depth) 445 | return \ 446 | canceled() \ 447 | if cancel != null and cancel.is_requested or is_canceled else \ 448 | self \ 449 | if depth == 0 else \ 450 | NesinkronaUnwrapAsync.new(self, cancel, depth) 451 | 452 | ## この [Async] が完了した場合、 453 | ## 指定したコルーチン (もしくは関数) の戻り値を結果として完了する新たな [Async] を作成します。[br] 454 | ## [br] 455 | ## 前の結果を写像する場合 ([method Array.map] 的な使い方) や、 456 | ## 結果から別のコルーチンへ処理を渡す場合に使うことができます。[br] 457 | ## [br] 458 | ## [b]補足[/b][br] 459 | ## 第 1 引数 [code]coroutine[/code] は引数を 1 つ受け取る 460 | ## [Callable] でなくてはなりません。この引数には直前の結果が渡されます。[br] 461 | ## [br] 462 | ## [b]使い方[/b] 463 | ## [codeblock] 464 | ## var async := Async.completed(10).then(func(result): return result * result) 465 | ## print(await async.wait()) # 100 466 | ## [/codeblock] 467 | func then(coroutine: Callable, cancel: Cancel = null) -> Async: 468 | return NesinkronaThenAsync.new(self, cancel, coroutine) 469 | 470 | ## この [Async] が完了した場合、 471 | ## 指定したコルーチン (もしくは関数) を呼び出し結果を待機する新たな [Async] を作成します。[br] 472 | ## [br] 473 | ## [b]補足[/b][br] 474 | ## 第 1 引数 [code]coroutine[/code] は引数を 3 つ受け取る 475 | ## [Callable] でなくてはなりません。この引数には直前の結果、 476 | ## 完了コールバック、キャンセルコールバックが渡されます。[br] 477 | ## [br] 478 | ## [b]使い方[/b] 479 | ## [codeblock] 480 | ## var async := Async.completed(10).then_callback(func(result, complete: Callable, cancel: Callable): 481 | ## complete.call(result * result)) 482 | ## print(await async.wait()) # 100 483 | ## [/codeblock] 484 | func then_callback(coroutine: Callable, cancel: Cancel = null, unbind_cancel := false) -> Async: 485 | return NesinkronaThenCallbackAsync.new(self, cancel, coroutine, unbind_cancel) 486 | 487 | ## [b](アドオン内もしくは実装内でのみ使用)[/b] 488 | ## この関数ではなく [member is_completed] もしくは [member is_canceled] を 489 | ## 使うようにしてください。将来名前が変わる可能性があります。 490 | func get_state() -> int: 491 | # 492 | # 継承先で実装する必要があります。 493 | # 494 | 495 | assert(false) 496 | return STATE_PENDING 497 | 498 | ## この待機可能な単位が完了もしくはキャンセルされるまで待機しその結果を返します。[br] 499 | ## [br] 500 | ## キャンセルされた場合の結果は必ず null となりますが、これは 501 | ## キャンセルされたかどうかの判断にはなりません。キャンセルされる可能性がある場合、 502 | ## [member is_completed] もしくは [member is_canceled] を確認する必要があります。[br] 503 | ## [br] 504 | ## [b]補足[/b][br] 505 | ## 状態が [STATE_PENDING] もしくは [STATE_PENDING_WITH_WAITERS] のとき 506 | ## 第 1 引数 [code]cancel[/code] に [Cancel] を指定しキャンセルを要求すると、 507 | ## 状態は [STATE_CANCELED] へ移行します。[br] 508 | ## [br] 509 | ## [b]使い方[/b] 510 | ## [codeblock] 511 | ## var async: Async = ... 512 | ## var result = async.wait() 513 | ## [/codeblock] 514 | func wait(cancel: Cancel = null) -> Variant: 515 | # 516 | # 継承先で実装する必要があります。 517 | # 518 | 519 | assert(false) 520 | return await null 521 | 522 | #------------------------------------------------------------------------------- 523 | 524 | func _to_string() -> String: 525 | var str: String 526 | match get_state(): 527 | STATE_PENDING: 528 | str = "(pending)" 529 | STATE_PENDING_WITH_WAITERS: 530 | str = "(pending_with_waiters)" 531 | STATE_CANCELED: 532 | str = "(canceled)" 533 | STATE_COMPLETED: 534 | str = "(completed)" 535 | _: 536 | assert(false) 537 | return str + "" % get_instance_id() 538 | -------------------------------------------------------------------------------- /Test_Task.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | signal _signal0 4 | signal _signal1(arg1: Variant) 5 | signal _signal2(arg1: Variant, arg2: Variant) 6 | signal _signal3(arg1: Variant, arg2: Variant, arg3: Variant) 7 | signal _signal4(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant) 8 | signal _signal5(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant, arg5: Variant) 9 | 10 | @onready var _tree := get_tree() 11 | 12 | func _emit0() -> void: 13 | _signal0.emit() 14 | 15 | func _emit1(arg1: Variant) -> void: 16 | _signal1.emit(arg1) 17 | 18 | func _emit2(arg1: Variant, arg2: Variant) -> void: 19 | _signal2.emit(arg1, arg2) 20 | 21 | func _emit3(arg1: Variant, arg2: Variant, arg3: Variant) -> void: 22 | _signal3.emit(arg1, arg2, arg3) 23 | 24 | func _emit4(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant) -> void: 25 | _signal4.emit(arg1, arg2, arg3, arg4) 26 | 27 | func _emit5(arg1: Variant, arg2: Variant, arg3: Variant, arg4: Variant, arg5: Variant) -> void: 28 | _signal5.emit(arg1, arg2, arg3, arg4, arg5) 29 | 30 | func _delay_emit0(timeout: float) -> void: 31 | _tree.create_timer(timeout).timeout.connect(_emit0) 32 | 33 | func _delay_emit1(timeout: float, arg1) -> void: 34 | _tree.create_timer(timeout).timeout.connect(_emit1.bind(arg1)) 35 | 36 | func _delay_emit2(timeout: float, arg1, arg2) -> void: 37 | _tree.create_timer(timeout).timeout.connect(_emit2.bind(arg1, arg2)) 38 | 39 | func _delay_emit3(timeout: float, arg1, arg2, arg3) -> void: 40 | _tree.create_timer(timeout).timeout.connect(_emit3.bind(arg1, arg2, arg3)) 41 | 42 | func _delay_emit4(timeout: float, arg1, arg2, arg3, arg4) -> void: 43 | _tree.create_timer(timeout).timeout.connect(_emit4.bind(arg1, arg2, arg3, arg4)) 44 | 45 | func _delay_emit5(timeout: float, arg1, arg2, arg3, arg4, arg5) -> void: 46 | _tree.create_timer(timeout).timeout.connect(_emit5.bind(arg1, arg2, arg3, arg4, arg5)) 47 | 48 | func _delay(timeout := 0.2) -> void: 49 | await _tree.create_timer(timeout).timeout 50 | 51 | func _delay_cancel(timeout := 0.1) -> Cancel: 52 | var cancel := Cancel.new() 53 | _tree.create_timer(timeout).timeout.connect(cancel.request) 54 | return cancel 55 | 56 | func _equal(array1: Array, array2: Array) -> bool: 57 | if array1.size() != array2.size(): 58 | return false 59 | for i in array1.size(): 60 | if array1[i] != array2[i]: 61 | return false 62 | return true 63 | 64 | func _test_completed() -> void: 65 | var async := Async.completed() 66 | assert(async.is_completed) 67 | assert(not async.is_canceled) 68 | assert(await async.wait() == null) 69 | async = Async.completed(123) 70 | assert(async.is_completed) 71 | assert(not async.is_canceled) 72 | assert(await async.wait() == 123) 73 | 74 | %Completed.button_pressed = true 75 | 76 | func _test_canceled() -> void: 77 | var async := Async.canceled() 78 | assert(not async.is_completed) 79 | assert(async.is_canceled) 80 | assert(await async.wait() == null) 81 | 82 | %Canceled.button_pressed = true 83 | 84 | func _test_from() -> void: 85 | var async := Async.from(func (): pass) 86 | assert(async.is_completed) 87 | assert(await async.wait() == null) 88 | 89 | async = Async.from(func (): return 123) 90 | assert(async.is_completed) 91 | assert(await async.wait() == 123) 92 | 93 | async = Async.from(func(): await _delay()) 94 | assert(not async.is_completed) 95 | assert(await async.wait() == null) 96 | assert(async.is_completed) 97 | 98 | async = Async.from(func (): await _delay(); return 123) 99 | assert(not async.is_completed) 100 | assert(await async.wait() == 123) 101 | assert(async.is_completed) 102 | 103 | async = Async.from(func(): await _delay()) 104 | assert(not async.is_completed) 105 | assert(await async.wait(_delay_cancel()) == null) 106 | assert(async.is_canceled) 107 | 108 | async = Async.from(func (): await _delay(); return 123) 109 | assert(not async.is_completed) 110 | assert(await async.wait(_delay_cancel()) == null) 111 | assert(async.is_canceled) 112 | 113 | %From.button_pressed = true 114 | 115 | func _test_from_signal() -> void: 116 | var async := Async.from_signal(_signal0) 117 | assert(not async.is_completed) 118 | _emit0() 119 | assert(_equal(await async.wait(), [])) 120 | assert(async.is_completed) 121 | 122 | async = Async.from_signal(_signal1, 1) 123 | assert(not async.is_completed) 124 | _emit1(123) 125 | assert(_equal(await async.wait(), [123])) 126 | assert(async.is_completed) 127 | 128 | async = Async.from_signal(_signal2, 2) 129 | assert(not async.is_completed) 130 | _emit2(123, "abc") 131 | assert(_equal(await async.wait(), [123, "abc"])) 132 | assert(async.is_completed) 133 | 134 | async = Async.from_signal(_signal3, 3) 135 | assert(not async.is_completed) 136 | _emit3(123, "abc", true) 137 | assert(_equal(await async.wait(), [123, "abc", true])) 138 | assert(async.is_completed) 139 | 140 | async = Async.from_signal(_signal4, 4) 141 | assert(not async.is_completed) 142 | _emit4(123, "abc", true, null) 143 | assert(_equal(await async.wait(), [123, "abc", true, null])) 144 | assert(async.is_completed) 145 | 146 | async = Async.from_signal(_signal5, 5) 147 | assert(not async.is_completed) 148 | _emit5(123, "abc", true, null, 0.5) 149 | assert(_equal(await async.wait(), [123, "abc", true, null, 0.5])) 150 | assert(async.is_completed) 151 | 152 | async = Async.from_signal(_signal0) 153 | assert(not async.is_completed) 154 | _delay_emit0(0.2) 155 | assert(await async.wait(_delay_cancel()) == null) 156 | assert(async.is_canceled) 157 | 158 | async = Async.from_signal(_signal1, 1) 159 | assert(not async.is_completed) 160 | _delay_emit1(0.2, 123) 161 | assert(await async.wait(_delay_cancel()) == null) 162 | assert(async.is_canceled) 163 | 164 | async = Async.from_signal(_signal2, 2) 165 | assert(not async.is_completed) 166 | _delay_emit2(0.2, 123, "abc") 167 | assert(await async.wait(_delay_cancel()) == null) 168 | assert(async.is_canceled) 169 | 170 | async = Async.from_signal(_signal3, 3) 171 | assert(not async.is_completed) 172 | _delay_emit3(0.2, 123, "abc", true) 173 | assert(await async.wait(_delay_cancel()) == null) 174 | assert(async.is_canceled) 175 | 176 | async = Async.from_signal(_signal4, 4) 177 | assert(not async.is_completed) 178 | _delay_emit4(0.2, 123, "abc", true, null) 179 | assert(await async.wait(_delay_cancel()) == null) 180 | assert(async.is_canceled) 181 | 182 | async = Async.from_signal(_signal5, 5) 183 | assert(not async.is_completed) 184 | _delay_emit5(0.2, 123, "abc", true, null, 0.5) 185 | assert(await async.wait(_delay_cancel()) == null) 186 | assert(async.is_canceled) 187 | 188 | %FromSignal.button_pressed = true 189 | 190 | func _test_from_signal_name() -> void: 191 | var async := Async.from_signal_name(self, "_signal0") 192 | assert(not async.is_completed) 193 | _emit0() 194 | assert(_equal(await async.wait(), [])) 195 | assert(async.is_completed) 196 | 197 | async = Async.from_signal_name(self, "_signal1", 1) 198 | assert(not async.is_completed) 199 | _emit1(123) 200 | assert(_equal(await async.wait(), [123])) 201 | assert(async.is_completed) 202 | 203 | async = Async.from_signal_name(self, "_signal2", 2) 204 | assert(not async.is_completed) 205 | _emit2(123, "abc") 206 | assert(_equal(await async.wait(), [123, "abc"])) 207 | assert(async.is_completed) 208 | 209 | async = Async.from_signal_name(self, "_signal3", 3) 210 | assert(not async.is_completed) 211 | _emit3(123, "abc", true) 212 | assert(_equal(await async.wait(), [123, "abc", true])) 213 | assert(async.is_completed) 214 | 215 | async = Async.from_signal_name(self, "_signal4", 4) 216 | assert(not async.is_completed) 217 | _emit4(123, "abc", true, null) 218 | assert(_equal(await async.wait(), [123, "abc", true, null])) 219 | assert(async.is_completed) 220 | 221 | async = Async.from_signal_name(self, "_signal5", 5) 222 | assert(not async.is_completed) 223 | _emit5(123, "abc", true, null, 0.5) 224 | assert(_equal(await async.wait(), [123, "abc", true, null, 0.5])) 225 | assert(async.is_completed) 226 | 227 | async = Async.from_signal_name(self, "_signal0") 228 | assert(not async.is_completed) 229 | _delay_emit0(0.2) 230 | assert(await async.wait(_delay_cancel()) == null) 231 | assert(async.is_canceled) 232 | 233 | async = Async.from_signal_name(self, "_signal1", 1) 234 | assert(not async.is_completed) 235 | _delay_emit1(0.2, 123) 236 | assert(await async.wait(_delay_cancel()) == null) 237 | assert(async.is_canceled) 238 | 239 | async = Async.from_signal_name(self, "_signal2", 2) 240 | assert(not async.is_completed) 241 | _delay_emit2(0.2, 123, "abc") 242 | assert(await async.wait(_delay_cancel()) == null) 243 | assert(async.is_canceled) 244 | 245 | async = Async.from_signal_name(self, "_signal3", 3) 246 | assert(not async.is_completed) 247 | _delay_emit3(0.2, 123, "abc", true) 248 | assert(await async.wait(_delay_cancel()) == null) 249 | assert(async.is_canceled) 250 | 251 | async = Async.from_signal_name(self, "_signal4", 4) 252 | assert(not async.is_completed) 253 | _delay_emit4(0.2, 123, "abc", true, null) 254 | assert(await async.wait(_delay_cancel()) == null) 255 | assert(async.is_canceled) 256 | 257 | async = Async.from_signal_name(self, "_signal5", 5) 258 | assert(not async.is_completed) 259 | _delay_emit5(0.2, 123, "abc", true, null, 0.5) 260 | assert(await async.wait(_delay_cancel()) == null) 261 | assert(async.is_canceled) 262 | 263 | %FromSignalName.button_pressed = true 264 | 265 | func _test_delay() -> void: 266 | var async := Async.delay(0.1) 267 | assert(not async.is_completed) 268 | assert(await async.wait() == 0.1) 269 | 270 | async = Async.delay(0.2) 271 | assert(not async.is_completed) 272 | assert(await async.wait(_delay_cancel()) == null) 273 | assert(async.is_canceled) 274 | 275 | %Delay.button_pressed = true 276 | 277 | func _test_delay_msec() -> void: 278 | var async := Async.delay_msec(100.0) 279 | assert(not async.is_completed) 280 | assert(await async.wait() == 0.1) 281 | 282 | async = Async.delay_msec(200.0) 283 | assert(not async.is_completed) 284 | assert(await async.wait(_delay_cancel()) == null) 285 | assert(async.is_canceled) 286 | 287 | %DelayMsec.button_pressed = true 288 | 289 | func _test_delay_usec() -> void: 290 | var async := Async.delay_usec(100000.0) 291 | assert(not async.is_completed) 292 | assert(await async.wait() == 0.1) 293 | 294 | async = Async.delay_usec(200000.0) 295 | assert(not async.is_completed) 296 | assert(await async.wait(_delay_cancel()) == null) 297 | assert(async.is_canceled) 298 | 299 | %DelayUsec.button_pressed = true 300 | 301 | func _test_all() -> void: 302 | var async := Async.all([]) 303 | assert(async.is_completed) 304 | assert(_equal(await async.wait(), [])) 305 | 306 | async = Async.all([123, "abc", true]) 307 | assert(async.is_completed) 308 | assert(_equal(await async.wait(), [123, "abc", true])) 309 | 310 | async = Async.all([Async.completed(123), Async.completed("abc"), Async.completed(true)]) 311 | assert(async.is_completed) 312 | assert(_equal(await async.wait(), [123, "abc", true])) 313 | 314 | async = Async.all([Async.completed(123), "abc", Async.completed(true)]) 315 | assert(async.is_completed) 316 | assert(_equal(await async.wait(), [123, "abc", true])) 317 | 318 | async = Async.all([Async.canceled(), Async.canceled(), Async.canceled()]) 319 | assert(async.is_canceled) 320 | assert(await async.wait() == null) 321 | 322 | async = Async.all([Async.completed(123), "abc", Async.canceled()]) 323 | assert(async.is_canceled) 324 | assert(await async.wait() == null) 325 | 326 | async = Async.all([Async.delay(0.2), Async.delay(0.2), Async.delay(0.2)]) 327 | assert(not async.is_completed) 328 | assert(_equal(await async.wait(), [0.2, 0.2, 0.2])) 329 | 330 | async = Async.all([Async.delay(0.2), Async.delay(0.2), Async.delay(0.2)], Cancel.canceled()) 331 | assert(async.is_canceled) 332 | assert(await async.wait() == null) 333 | 334 | async = Async.all([Async.delay(0.2), "abc", Async.completed(true)]) 335 | assert(not async.is_completed) 336 | assert(_equal(await async.wait(), [0.2, "abc", true])) 337 | 338 | async = Async.all([Async.delay(0.2), "abc", Async.completed(true)], _delay_cancel()) 339 | assert(not async.is_completed) 340 | assert(await async.wait() == null) 341 | assert(async.is_canceled) 342 | 343 | async = Async.all([Async.delay(0.2), "abc", Async.completed(true)]) 344 | assert(not async.is_completed) 345 | assert(await async.wait(_delay_cancel()) == null) 346 | assert(async.is_canceled) 347 | 348 | %All.button_pressed = true 349 | 350 | func _test_all_settled() -> void: 351 | var async := Async.all_settled([]) 352 | assert(async.is_completed) 353 | var list = await async.wait() 354 | assert(list.size() == 0) 355 | 356 | async = Async.all_settled([123, "abc", true]) 357 | assert(async.is_completed) 358 | list = await async.wait() 359 | assert(list.size() == 3) 360 | assert(await list[0].wait() == 123) 361 | assert(await list[1].wait() == "abc") 362 | assert(await list[2].wait()) 363 | 364 | async = Async.all_settled([Async.completed(123), Async.completed("abc"), Async.completed(true)]) 365 | assert(async.is_completed) 366 | list = await async.wait() 367 | assert(list.size() == 3) 368 | assert(await list[0].wait() == 123) 369 | assert(await list[1].wait() == "abc") 370 | assert(await list[2].wait()) 371 | 372 | async = Async.all_settled([Async.completed(123), "abc", Async.completed(true)]) 373 | assert(async.is_completed) 374 | list = await async.wait() 375 | assert(list.size() == 3) 376 | assert(await list[0].wait() == 123) 377 | assert(await list[1].wait() == "abc") 378 | assert(await list[2].wait()) 379 | 380 | async = Async.all_settled([Async.canceled(), Async.canceled(), Async.canceled()]) 381 | assert(async.is_completed) 382 | list = await async.wait() 383 | assert(list.size() == 3) 384 | assert(list[0].is_canceled) 385 | assert(list[1].is_canceled) 386 | assert(list[2].is_canceled) 387 | 388 | async = Async.all_settled([Async.completed(123), "abc", Async.canceled()]) 389 | assert(async.is_completed) 390 | list = await async.wait() 391 | assert(list.size() == 3) 392 | assert(await list[0].wait() == 123) 393 | assert(await list[1].wait() == "abc") 394 | assert(list[2].is_canceled) 395 | 396 | async = Async.all_settled([Async.delay(0.1), Async.delay(0.1), Async.delay(0.1)]) 397 | assert(not async.is_completed) 398 | list = await async.wait() 399 | assert(list.size() == 3) 400 | assert(await list[0].wait() == 0.1) 401 | assert(await list[1].wait() == 0.1) 402 | assert(await list[2].wait() == 0.1) 403 | 404 | async = Async.all_settled([Async.delay(0.1), "abc", Async.completed(true)]) 405 | assert(not async.is_completed) 406 | list = await async.wait() 407 | assert(list.size() == 3) 408 | assert(await list[0].wait() == 0.1) 409 | assert(await list[1].wait() == "abc") 410 | assert(await list[2].wait()) 411 | 412 | async = Async.all_settled([Async.delay(0.2), "abc", Async.completed(true)], _delay_cancel()) 413 | assert(not async.is_completed) 414 | list = await async.wait() 415 | assert(list[0].is_canceled) 416 | assert(await list[1].wait() == "abc") 417 | assert(await list[2].wait()) 418 | 419 | async = Async.all_settled([Async.delay(0.2), "abc", Async.completed(true)]) 420 | assert(not async.is_completed) 421 | assert(await async.wait(_delay_cancel()) == null) 422 | assert(async.is_canceled) 423 | 424 | %AllSettled.button_pressed = true 425 | 426 | func _test_any() -> void: 427 | var async := Async.any([]) 428 | assert(async.is_canceled) 429 | assert(await async.wait() == null) 430 | 431 | async = Async.any([123, "abc", true]) 432 | assert(async.is_completed) 433 | assert(await async.wait() == 123) 434 | 435 | async = Async.any([Async.completed(123), Async.completed("abc"), Async.completed(true)]) 436 | assert(async.is_completed) 437 | assert(await async.wait() == 123) 438 | 439 | async = Async.any([Async.completed(123), "abc", Async.completed(true)]) 440 | assert(async.is_completed) 441 | assert(await async.wait() == 123) 442 | 443 | async = Async.any([Async.canceled(), Async.canceled(), Async.canceled()]) 444 | assert(async.is_canceled) 445 | assert(await async.wait() == null) 446 | 447 | async = Async.any([Async.completed(123), "abc", Async.canceled()]) 448 | assert(async.is_completed) 449 | assert(await async.wait() == 123) 450 | 451 | async = Async.any([Async.delay(0.1), Async.delay(0.1), Async.delay(0.1)]) 452 | assert(not async.is_completed) 453 | assert(await async.wait() == 0.1) 454 | assert(async.is_completed) 455 | 456 | async = Async.any([Async.delay(0.1), "abc", Async.completed(true)]) 457 | assert(async.is_completed) 458 | assert(await async.wait() == "abc") 459 | 460 | async = Async.any([Async.delay(0.1), "abc", Async.canceled()], _delay_cancel()) 461 | assert(async.is_completed) 462 | assert(await async.wait() == "abc") 463 | 464 | async = Async.any([Async.delay(0.2), Async.delay(0.2), Async.delay(0.2)], _delay_cancel()) 465 | assert(not async.is_completed) 466 | assert(await async.wait() == null) 467 | assert(async.is_canceled) 468 | 469 | async = Async.any([Async.delay(0.2), Async.delay(0.2), Async.delay(0.2)]) 470 | assert(not async.is_completed) 471 | assert(await async.wait(_delay_cancel()) == null) 472 | assert(async.is_canceled) 473 | 474 | async = Async.any([123, "abc", true]) 475 | assert(async.is_completed) 476 | assert(await async.wait(Cancel.canceled()) == 123) 477 | assert(async.is_completed) 478 | 479 | %Any.button_pressed = true 480 | 481 | func _test_race() -> void: 482 | # var async := Async.race([]) 483 | # assert(async.is_completed) 484 | # assert(await async.wait() == null) 485 | 486 | var async := Async.race([123, "abc", true]) 487 | assert(async.is_completed) 488 | async = await async.wait() 489 | assert(async is Async) 490 | assert(await async.wait() == 123) 491 | 492 | async = Async.race([Async.completed(123), Async.completed("abc"), Async.completed(true)]) 493 | assert(async.is_completed) 494 | async = await async.wait() 495 | assert(await async.wait() == 123) 496 | 497 | async = Async.race([Async.completed(123), "abc", Async.completed(true)]) 498 | assert(async.is_completed) 499 | async = await async.wait() 500 | assert(await async.wait() == 123) 501 | 502 | async = Async.race([Async.canceled(), Async.canceled(), Async.canceled()]) 503 | assert(async.is_completed) 504 | async = await async.wait() 505 | assert(async.is_canceled) 506 | 507 | async = Async.race([Async.completed(123), "abc", Async.canceled()]) 508 | assert(async.is_completed) 509 | async = await async.wait() 510 | assert(await async.wait() == 123) 511 | 512 | async = Async.race([Async.canceled(), "abc", Async.completed(true)]) 513 | assert(async.is_completed) 514 | async = await async.wait() 515 | assert(await async.wait() == "abc") 516 | 517 | async = Async.race([Async.delay(0.1), Async.delay(0.1), Async.delay(0.1)]) 518 | assert(not async.is_completed) 519 | async = await async.wait() 520 | assert(await async.wait() == 0.1) 521 | 522 | async = Async.race([Async.delay(0.1), Async.delay(0.1), Async.delay(0.1)], Cancel.canceled()) 523 | assert(async.is_completed) 524 | async = await async.wait() 525 | assert(async.is_canceled) 526 | 527 | async = Async.race([Async.delay(0.1), "abc", Async.completed(true)]) 528 | assert(async.is_completed) 529 | async = await async.wait() 530 | assert(await async.wait() == "abc") 531 | 532 | async = Async.race([Async.delay(0.2), "abc", Async.completed(true)], _delay_cancel()) 533 | assert(async.is_completed) 534 | async = await async.wait() 535 | assert(await async.wait() == "abc") 536 | 537 | async = Async.race([Async.delay(0.2), "abc", Async.completed(true)]) 538 | assert(async.is_completed) 539 | async = await async.wait(_delay_cancel()) 540 | assert(await async.wait() == "abc") 541 | 542 | async = Async.race([Async.delay(0.2), "abc", Async.completed(true)], _delay_cancel()) 543 | assert(async.is_completed) 544 | async = await async.wait(_delay_cancel()) 545 | assert(await async.wait() == "abc") 546 | 547 | %Race.button_pressed = true 548 | 549 | func _test_then() -> void: 550 | var async := Async.completed(123).then(func (r): return r * r) 551 | assert(async.is_completed) 552 | assert(await async.wait() == 15129) 553 | 554 | async = Async.canceled().then(func (r): return r * r) 555 | assert(async.is_canceled) 556 | assert(await async.wait() == null) 557 | 558 | async = Async.delay(0.1).then(func (_r): return 15129) 559 | assert(not async.is_completed) 560 | assert(await async.wait() == 15129) 561 | 562 | async = Async.completed(123).then(func (r): 563 | await Async.wait_delay(0.1) 564 | return r * r) 565 | assert(not async.is_completed) 566 | assert(await async.wait() == 15129) 567 | 568 | async = Async.delay(0.2).then(func (r): return r * r, _delay_cancel()) 569 | assert(not async.is_completed) 570 | assert(not async.is_canceled) 571 | assert(await async.wait() == null) 572 | assert(async.is_canceled) 573 | 574 | async = Async.delay(0.2).then(func (r): return r * r) 575 | assert(not async.is_completed) 576 | assert(not async.is_canceled) 577 | assert(await async.wait(_delay_cancel()) == null) 578 | assert(async.is_canceled) 579 | 580 | async = Async.delay(0.2).then(func (r): return r * r, _delay_cancel()) 581 | assert(not async.is_completed) 582 | assert(not async.is_canceled) 583 | assert(await async.wait(_delay_cancel()) == null) 584 | assert(async.is_canceled) 585 | 586 | async = Async.delay(0.2).then(func (r): return r * r, Cancel.canceled()) 587 | assert(not async.is_completed) 588 | assert(async.is_canceled) 589 | assert(await async.wait(Cancel.canceled()) == null) 590 | 591 | %Then.button_pressed = true 592 | 593 | func _test_unwrap() -> void: 594 | var async := Async.completed(123).unwrap() 595 | assert(async.is_completed) 596 | assert(await async.wait() == 123) 597 | 598 | async = Async.completed(Async.completed(123)).unwrap() 599 | assert(async.is_completed) 600 | assert(await async.wait() == 123) 601 | 602 | async = Async.completed(Async.completed(Async.completed(123))).unwrap(2) 603 | assert(async.is_completed) 604 | assert(await async.wait() == 123) 605 | 606 | async = Async.canceled().unwrap() 607 | assert(async.is_canceled) 608 | assert(await async.wait() == null) 609 | 610 | async = Async.completed(Async.delay(0.1)).unwrap() 611 | assert(not async.is_completed) 612 | assert(await async.wait() == 0.1) 613 | 614 | async = Async.completed(Async.delay(0.2)).unwrap(1, _delay_cancel(0.1)) 615 | assert(not async.is_completed) 616 | assert(not async.is_canceled) 617 | assert(await async.wait() == null) 618 | assert(async.is_canceled) 619 | 620 | async = Async.completed(Async.completed(Async.delay(0.2))).unwrap(1, _delay_cancel(0.1)) 621 | assert(async.is_completed) 622 | async = await async.wait() 623 | assert(not async.is_completed) 624 | assert(await async.wait() == 0.2) 625 | 626 | %Unwrap.button_pressed = true 627 | 628 | func _test_from_conditional_signal() -> void: 629 | var async := Async.from_conditional_signal(_signal0, []) 630 | assert(not async.is_completed) 631 | _emit0() 632 | assert(_equal(await async.wait(), [])) 633 | 634 | async = Async.from_conditional_signal(_signal1, [123]) 635 | assert(not async.is_completed) 636 | _emit1(456) 637 | assert(not async.is_completed) 638 | _emit1(123) 639 | assert(_equal(await async.wait(), [123])) 640 | 641 | async = Async.from_conditional_signal(_signal1, [Async.SKIP]) 642 | assert(not async.is_completed) 643 | _emit1(123) 644 | assert(_equal(await async.wait(), [123])) 645 | 646 | async = Async.from_conditional_signal(_signal2, [123, "abc"]) 647 | assert(not async.is_completed) 648 | _emit2(123, "def") 649 | assert(not async.is_completed) 650 | _emit2(123, "abc") 651 | assert(_equal(await async.wait(), [123, "abc"])) 652 | 653 | async = Async.from_conditional_signal(_signal2, [123, Async.SKIP]) 654 | assert(not async.is_completed) 655 | _emit2(123, "abc") 656 | assert(_equal(await async.wait(), [123, "abc"])) 657 | 658 | async = Async.from_conditional_signal(_signal3, [123, "abc", true]) 659 | assert(not async.is_completed) 660 | _emit3(123, "abc", false) 661 | assert(not async.is_completed) 662 | _emit3(123, "abc", true) 663 | assert(_equal(await async.wait(), [123, "abc", true])) 664 | 665 | async = Async.from_conditional_signal(_signal3, [123, "abc", Async.SKIP]) 666 | assert(not async.is_completed) 667 | _emit3(123, "abc", true) 668 | assert(_equal(await async.wait(), [123, "abc", true])) 669 | 670 | async = Async.from_conditional_signal(_signal4, [123, "abc", true, null]) 671 | assert(not async.is_completed) 672 | _emit4(123, "abc", true, Object.new()) 673 | assert(not async.is_completed) 674 | _emit4(123, "abc", true, null) 675 | assert(_equal(await async.wait(), [123, "abc", true, null])) 676 | 677 | async = Async.from_conditional_signal(_signal4, [123, "abc", true, Async.SKIP]) 678 | assert(not async.is_completed) 679 | _emit4(123, "abc", true, null) 680 | assert(_equal(await async.wait(), [123, "abc", true, null])) 681 | 682 | async = Async.from_conditional_signal(_signal5, [123, "abc", true, null, 0.5]) 683 | assert(not async.is_completed) 684 | _emit5(123, "abc", true, null, 1.0) 685 | assert(not async.is_completed) 686 | _emit5(123, "abc", true, null, 0.5) 687 | assert(_equal(await async.wait(), [123, "abc", true, null, 0.5])) 688 | 689 | async = Async.from_conditional_signal(_signal5, [123, "abc", true, null, Async.SKIP]) 690 | assert(not async.is_completed) 691 | _emit5(123, "abc", true, null, 0.5) 692 | assert(_equal(await async.wait(), [123, "abc", true, null, 0.5])) 693 | 694 | %FromConditionalSignal.button_pressed = true 695 | 696 | func _test_from_conditional_signal_name() -> void: 697 | var async := Async.from_conditional_signal_name(self, "_signal0", []) 698 | assert(not async.is_completed) 699 | _emit0() 700 | assert(_equal(await async.wait(), [])) 701 | 702 | async = Async.from_conditional_signal_name(self, "_signal1", [123]) 703 | assert(not async.is_completed) 704 | _emit1(456) 705 | assert(not async.is_completed) 706 | _emit1(123) 707 | assert(_equal(await async.wait(), [123])) 708 | 709 | async = Async.from_conditional_signal_name(self, "_signal1", [Async.SKIP]) 710 | assert(not async.is_completed) 711 | _emit1(123) 712 | assert(_equal(await async.wait(), [123])) 713 | 714 | async = Async.from_conditional_signal_name(self, "_signal2", [123, "abc"]) 715 | assert(not async.is_completed) 716 | _emit2(123, "def") 717 | assert(not async.is_completed) 718 | _emit2(123, "abc") 719 | assert(_equal(await async.wait(), [123, "abc"])) 720 | 721 | async = Async.from_conditional_signal_name(self, "_signal2", [123, Async.SKIP]) 722 | assert(not async.is_completed) 723 | _emit2(123, "abc") 724 | assert(_equal(await async.wait(), [123, "abc"])) 725 | 726 | async = Async.from_conditional_signal_name(self, "_signal3", [123, "abc", true]) 727 | assert(not async.is_completed) 728 | _emit3(123, "abc", false) 729 | assert(not async.is_completed) 730 | _emit3(123, "abc", true) 731 | assert(_equal(await async.wait(), [123, "abc", true])) 732 | 733 | async = Async.from_conditional_signal_name(self, "_signal3", [123, "abc", Async.SKIP]) 734 | assert(not async.is_completed) 735 | _emit3(123, "abc", true) 736 | assert(_equal(await async.wait(), [123, "abc", true])) 737 | 738 | async = Async.from_conditional_signal_name(self, "_signal4", [123, "abc", true, null]) 739 | assert(not async.is_completed) 740 | _emit4(123, "abc", true, Object.new()) 741 | assert(not async.is_completed) 742 | _emit4(123, "abc", true, null) 743 | assert(_equal(await async.wait(), [123, "abc", true, null])) 744 | 745 | async = Async.from_conditional_signal_name(self, "_signal4", [123, "abc", true, Async.SKIP]) 746 | assert(not async.is_completed) 747 | _emit4(123, "abc", true, null) 748 | assert(_equal(await async.wait(), [123, "abc", true, null])) 749 | 750 | async = Async.from_conditional_signal_name(self, "_signal5", [123, "abc", true, null, 0.5]) 751 | assert(not async.is_completed) 752 | _emit5(123, "abc", true, null, 1.0) 753 | assert(not async.is_completed) 754 | _emit5(123, "abc", true, null, 0.5) 755 | assert(_equal(await async.wait(), [123, "abc", true, null, 0.5])) 756 | 757 | async = Async.from_conditional_signal_name(self, "_signal5", [123, "abc", true, null, Async.SKIP]) 758 | assert(not async.is_completed) 759 | _emit5(123, "abc", true, null, 0.5) 760 | assert(_equal(await async.wait(), [123, "abc", true, null, 0.5])) 761 | 762 | %FromConditionalSignalName.button_pressed = true 763 | 764 | func _test_defer() -> void: 765 | var async := Async.defer(Async.DEFER_IDLE) 766 | assert(not async.is_completed) 767 | assert(await async.wait() == null) 768 | 769 | async = Async.defer(Async.DEFER_PROCESS_FRAME) 770 | assert(not async.is_completed) 771 | assert(await async.wait() == null) 772 | 773 | async = Async.defer(Async.DEFER_PHYSICS_FRAME) 774 | assert(not async.is_completed) 775 | assert(await async.wait() == null) 776 | 777 | %Defer.button_pressed = true 778 | 779 | func _ready() -> void: 780 | # 1.0.1 781 | await _test_completed() 782 | await _test_canceled() 783 | await _test_from() 784 | await _test_from_signal() 785 | await _test_from_signal_name() 786 | await _test_delay() 787 | await _test_delay_msec() 788 | await _test_delay_usec() 789 | await _test_all() 790 | await _test_all_settled() 791 | await _test_any() 792 | await _test_race() 793 | await _test_then() 794 | await _test_unwrap() 795 | 796 | # 1.0.2 797 | await _test_defer() 798 | await _test_from_conditional_signal() 799 | await _test_from_conditional_signal_name() 800 | --------------------------------------------------------------------------------