Get a post from other loop each n-posts in the main loop

Here’s what you can do. At the top of your page, run your first WP_Query. Calculate the number of posts you need for your secondary query by using the number of posts IN query cat_a divided by 3 and using PHP floor() to drop any remainder. This will ensure that your second query only has the exact number of posts needed to get the pattern your desire.

When displaying we can use PHP Modulus to display our cat_b post before cat_a 4 ( increments ) ever displays. In this case I enter The Loop of cat_b but break out so WordPress keeps track of where we are in our loop and we don’t need to.

Altogether, the idea looks something like this. I haven’t tested any of it so if you run into issues turn on debugging and see if you can fix any PHP errors.

$cat_b = '';
$cat_a = new WP_Query( array(
    'category'          => 'a',
    'posts_per_page'    => 60,
) );

if( $cat_a->have_posts() ) {
    $num_posts = floor( $cat_a->post_count / 3 );

    if( $num_posts > 0 ) {
        $cat_b = new WP_Query( array(
            'category'      => 'b',
            'posts_per_page'=> $num_posts,
        ) );
    }
}


if( $cat_a->have_posts() ) : $index = 1; ?>

    <?php while( $cat_a->have_posts() ) : $cat_a->the_post(); ?>

        <?php if( ! empty( $cat_b ) && 0 == $index % 4 && $cat_b->have_posts() ) : ?>

            <?php while( $cat_b->have_posts() ) : $cat_b->the_post();

                <h1><?php the_title(); ?></h1>

            <?php
                wp_reset_postdata();
                break;
                endwhile;
            ?>

        <?php endif; ?>

        <h1><?php the_title(); ?></h1>

    <?php
        $index++;
        endwhile;
    ?>

<?php
    wp_reset_postdata();
    endif;
?>