A sometimes useful way to handle cases like these is to connect to the node's ready or tree_entered signal, and do more of your setup there.
Your original code...
func spawn_enemy(x : int,y : int) -> void:
var enemy := enemy_data.instance()
get_parent().call_deferred("add_child", enemy)
enemy.set_deferred("global_position", Vector2(x,y)
...could be expressed as:
func spawn_enemy(x:int, y:int) -> void:
var enemy := enemy_data.instance()
get_parent().add_child.call_deferred(enemy)
enemy.ready.connect(func(): enemy.global_position = Vector2(x,y))
Note that this will run after the enemy's _ready() function, so if something in there depends on the position, you may run into an issue with this solution - maybe waiting for tree_entered is even more useful (to run before _ready(). Either way, waiting for a signal from the node solves the race-case better than firing two deferred functions.
Note that the shared code is Godot 4 (GDScript 2), but you can do something similar in Godot 3 as well.