Stuck in the query loop

<div class="pr_grid_cell clearfix">
<?php
//for each category, show posts
$categories=get_categories();
foreach($categories as $cat) {
$args=array(
  'post_type' => 'products',
  'showposts' => -1,
  'category__in' => array($cat->term_id)
);
$posts = new wp_query($args); // This is the best way to 
  if ($posts->have_posts()) while ($posts->have_posts()) { //condional if/while 2-in-1. If you dont check have_posts, you'll get an ugly error when nothings found. 
    the_post();//increments wps internal counter, Needed so that your loop doesnt run forever. 
    ?>

    <div class="pr_grid buy-online">
        <span class="hcenter">
        <h5><?= $cat->name ?></h5>
     <? foreach($posts as $post) {
      setup_postdata($post); ?>
        <? if( get_field('buy_online_href') ): ?>
                    <?php // create our link now that the post is setup ?>
                    <span class="pr_img_href" href="https://wordpress.stackexchange.com/questions/174106/<? the_permalink(); ?>">
                        <img class="ii" src="<?php the_field('product_thumbnail'); ?>">
                        <a class="button" target="_blank" href="<?php the_field('buy_online_href'); ?>">Buy Online</a>
                        <a class="button" target="_blank" href="http://wordpress.stackexchange.com/store-locator">Find Near you</a>
                    </span>
        <? endif ?>          
        <?php
    } // foreach($posts
  } // if ($posts
} // foreach($categories
?>
        </span>
    </div> 
</div>

As mentioned, avoid using query_posts. In pretty much every case, it’s cleaner to just instantiate your own instance of the wp_query. I think the one thing missing, which is why it was running infinitely, was the lack of the_post(). That incremements the “counter” that tells the loop when it’s done. Without it, it will just run indefinitely.