Set emitting = false to stop a looping effect: particles already alive finish their lifetime, then the effect is empty. To thin it out first, tween amount_ratio to 0. A looping effect did not fire finished in our test, so remove the node after a delay.

Stop without a fade
fx.emitting = false is enough. With a lifetime of 0.8 seconds, the particles thinned out right away and the screen was empty 1.4 seconds later. finished fired 0 times.
Fade, then remove the node
extends Node
## Fade out a looping effect, let the last particles finish, then remove it.
func fade_and_free(fx: GPUParticles3D, seconds := 1.0) -> void:
var t := fx.create_tween()
t.tween_property(fx, "amount_ratio", 0.0, seconds)
t.tween_callback(func(): fx.emitting = false)
t.tween_interval(fx.lifetime)
t.tween_callback(fx.queue_free)
In our test the effect thinned out during the 1 second tween. After that plus one lifetime, the screen was empty and the node was gone.
What amount_ratio does
It lowers how many particles are emitted. It does not make them see-through, and particles already alive are not changed. That is why the script waits one more lifetime before removing the node.
Tested in Godot 4.7 with the Compatibility renderer.
Want ready-made effects? Try the free Arcane VFX pack or the VFX Collection for Godot 4.