Usually functions have to be completed before the next frame can be displayed. Coroutines don't have that limitation and Godot's animation players offer a visual language especially for coroutines.
The following article is a short overview over the sample project. To fully understand the ideas check out the project files.
Assets from OpenGameArt.org
If you open up the scene "Player.tscn" you'll find the basic structure shown in the screenshot.
The two core elements are the root node of type Node2D and the attached AnimationPlayer.
All the other nodes are added for collision detection, so in order to understand how the grid movement works they can be ignored.
The root node is extended by a script. This script defines how the scene should react to input by filling in the _input()-method, and it adds a move_squares()-method.
The move_squares()-method simply moves the scene, and the movement is scaled by the size of the grid, so moving by an amount of (1,1) would change the position one square down and right.
The code in the _input()-method says that if no animation is already running and if the input event is a "ui_down"-event the animation player should start an animation (lines 18-21): If the raycast collides with anything it should start the "look_down" animation, otherwise the "down" animation.
In the project you'll of course find similar code and animations for the other three directions.
The "down"-animation performs the actual movement over several frames by advancing the animation frame (track 1) and moving the player down across one square (track 2).
In the last frame of the animation though it moves the player back to its original position andcalls a function on the call func track (track 3).
This key on the call func track is what actually moves the player from one grid square to the next.
If you edit the key you'll find that it calls the move_squares()-method with a parameter of(0,1), so it moves the player down one square (that's why the second animation track needs to reset the offset of the player sprite in the last frame).
This can only be done in a call to a GDscript function, because animations can only set absolute values. The second animation track sets the local position of the player sprite to values ranging from (0,0) to (0,16), but in the end the player needs to be moved relative to its start position, which is what the translate()-method inside the move_squares()-method does.
The "look_down"-animation is called when the player can't move down. In this example it only turns the player towards the obstacle, but it could easily be changed so that the player shrugs his shoulders to show that he can't move.
Alternative solution without a call func track
If you prefer you can change the order of the calls to remove the call func track.
In the solution described in this article the player moves towards the next square and when it arrives it's placed back at the origin (by the animation) and the whole scene is teleported to the target position (by the translate()-call).
Instead you can immediately translate the player to the target position and then start an animation that has it move from the previous position (0,-16) to the origin (0,0).