diff --git a/wp-includes/query.php b/wp-includes/query.php
index ad407c1..4ee3353 100644
--- a/wp-includes/query.php
+++ b/wp-includes/query.php
@@ -3623,11 +3623,6 @@ class WP_Query {
 		if ( $this->posts )
 			$this->posts = array_map( 'get_post', $this->posts );
 
-
-		if ( $q['update_post_term_cache'] ) {
-			add_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
-		}
-
 		if ( ! $q['suppress_filters'] ) {
 			/**
 			 * Filter the raw post results array, prior to status checks.
@@ -3781,6 +3776,9 @@ class WP_Query {
 
 			if ( $q['cache_results'] )
 				update_post_caches($this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache']);
+			if ( $q['update_post_term_cache'] ) {
+				handle_lazy_loads( $this->posts );
+			}
 
 			$this->post = reset( $this->posts );
 		} else {
@@ -4850,83 +4848,6 @@ class WP_Query {
 			$this->setup_postdata( $this->post );
 		}
 	}
-
-	/**
-	 * Lazy-loads termmeta for located posts.
-	 *
-	 * As a rule, term queries (`get_terms()` and `wp_get_object_terms()`) prime the metadata cache for matched
-	 * terms by default. However, this can cause a slight performance penalty, especially when that metadata is
-	 * not actually used. In the context of a `WP_Query` instance, we're able to avoid this potential penalty.
-	 * `update_object_term_cache()`, called from `update_post_caches()`, does not 'update_term_meta_cache'.
-	 * Instead, the first time `get_term_meta()` is called from within a `WP_Query` loop, the current method
-	 * detects the fact, and then primes the metadata cache for all terms attached to all posts in the loop,
-	 * with a single database query.
-	 *
-	 * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
-	 * directly, from either inside or outside the `WP_Query` object.
-	 *
-	 * @since 4.4.0
-	 * @access public
-	 *
-	 * @param mixed $check  The `$check` param passed from the 'get_term_metadata' hook.
-	 * @param int  $term_id ID of the term whose metadata is being cached.
-	 * @return mixed In order not to short-circuit `get_metadata()`. Generally, this is `null`, but it could be
-	 *               another value if filtered by a plugin.
-	 */
-	public function lazyload_term_meta( $check, $term_id ) {
-		/*
-		 * We only do this once per `WP_Query` instance.
-		 * Can't use `remove_filter()` because of non-unique object hashes.
-		 */
-		if ( $this->updated_term_meta_cache ) {
-			return $check;
-		}
-
-		// We can only lazyload if the entire post object is present.
-		$posts = array();
-		foreach ( $this->posts as $post ) {
-			if ( $post instanceof WP_Post ) {
-				$posts[] = $post;
-			}
-		}
-
-		if ( ! empty( $posts ) ) {
-			// Fetch cached term_ids for each post. Keyed by term_id for faster lookup.
-			$term_ids = array();
-			foreach ( $posts as $post ) {
-				$taxonomies = get_object_taxonomies( $post->post_type );
-				foreach ( $taxonomies as $taxonomy ) {
-					// Term cache should already be primed by 'update_post_term_cache'.
-					$terms = get_object_term_cache( $post->ID, $taxonomy );
-					if ( false !== $terms ) {
-						foreach ( $terms as $term ) {
-							if ( ! isset( $term_ids[ $term->term_id ] ) ) {
-								$term_ids[ $term->term_id ] = 1;
-							}
-						}
-					}
-				}
-			}
-
-			/*
-			 * Only update the metadata cache for terms belonging to these posts if the term_id passed
-			 * to `get_term_meta()` matches one of those terms. This prevents a single call to
-			 * `get_term_meta()` from priming metadata for all `WP_Query` objects.
-			 */
-			if ( isset( $term_ids[ $term_id ] ) ) {
-				update_termmeta_cache( array_keys( $term_ids ) );
-				$this->updated_term_meta_cache = true;
-			}
-		}
-
-		// If no terms were found, there's no need to run this again.
-		if ( empty( $term_ids ) ) {
-			$this->updated_term_meta_cache = true;
-		}
-
-		return $check;
-	}
-
 	/**
 	 * Lazy-load comment meta when inside of a `WP_Query` loop.
 	 *
@@ -5085,3 +5006,136 @@ function setup_postdata( $post ) {
 
 	return false;
 }
+
+/**
+ * Handle WP_Query term meta data lazy load.
+ *
+ * @since 4.5.0
+ *
+ * @param array $posts Collection posts.
+ */
+function handle_lazy_loads( array $posts ) {
+	static $wp_lazy_loader;
+	if ( null === $wp_lazy_loader ) {
+		$wp_lazy_loader = new WP_Lazy_Loader();
+	}
+	$wp_lazy_loader->add_posts( $posts );
+}
+
+/**
+ * The WordPress Query term meta data lazy load handler class.
+ *
+ * @since 4.5.0
+ */
+class WP_Lazy_Loader {
+
+	/**
+	 * Collection of managed posts.
+	 *
+	 * @since 4.5.0
+	 * @var array
+	 */
+	protected $posts = array();
+
+	/**
+	 * Collection of handled term ids.
+	 *
+	 * @since 4.5.0
+	 * @var array
+	 */
+	protected $updated_term_meta_cache = array();
+
+	/**
+	 * WP_Lazy_Loader constructor.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 */
+	public function __construct() {
+		add_filter( 'get_term_metadata', array( $this, 'lazyload_term_meta' ), 10, 2 );
+	}
+
+	/**
+	 * Add posts to internal collection.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 *
+	 * @param array $posts Collection of posts.
+	 */
+	public function add_posts( array $posts ) {
+
+		if ( empty( $posts ) ) {
+			return;
+		}
+		$this->updated_term_meta_cache = array();
+
+		foreach ( $posts as $post ) {
+			if ( isset( $this->posts[ $post->ID ] ) || ! $post instanceof WP_Post ) {
+				continue;
+			}
+			$this->posts[ $post->ID ] = $post;
+		}
+
+	}
+
+	/**
+	 * Lazy-loads termmeta for located posts.
+	 *
+	 * As a rule, term queries (`get_terms()` and `wp_get_object_terms()`) prime the metadata cache for matched
+	 * terms by default. However, this can cause a slight performance penalty, especially when that metadata is
+	 * not actually used. In the context of a `WP_Query` instance, we're able to avoid this potential penalty.
+	 * `update_object_term_cache()`, called from `update_post_caches()`, does not 'update_term_meta_cache'.
+	 * Instead, the first time `get_term_meta()` is called from within a `WP_Query` loop, the current method
+	 * detects the fact, and then primes the metadata cache for all terms attached to all posts in the loop,
+	 * with a single database query.
+	 *
+	 * This method is public so that it can be used as a filter callback. As a rule, there is no need to invoke it
+	 * directly, from either inside or outside the `WP_Query` object.
+	 *
+	 * @since 4.5.0
+	 * @access public
+	 *
+	 * @param mixed $check  The `$check` param passed from the 'get_term_metadata' hook.
+	 * @param int  $term_id ID of the term whose metadata is being cached.
+	 * @return mixed In order not to short-circuit `get_metadata()`. Generally, this is `null`, but it could be
+	 *               another value if filtered by a plugin.
+	 */
+	public function lazyload_term_meta( $check, $term_id ) {
+		/*
+		 * We only do this once per term ID.
+		 */
+		if ( isset( $this->updated_term_meta_cache[ $term_id ] ) ) {
+			return $check;
+		}
+
+		// Fetch cached term_ids for each post. Keyed by term_id for faster lookup.
+		$term_ids = array();
+		foreach ( $this->posts as $id => $post ) {
+			$taxonomies = get_object_taxonomies( $post->post_type );
+			foreach ( $taxonomies as $taxonomy ) {
+				// Term cache should already be primed by 'update_post_term_cache'.
+				$terms = get_object_term_cache( $post->ID, $taxonomy );
+				if ( false !== $terms ) {
+					foreach ( $terms as $term ) {
+						if ( ! isset( $term_ids[ $term->term_id ] ) ) {
+							$term_ids[ $term->term_id ] = 1;
+						}
+					}
+				}
+			}
+		}
+		/*
+		 * Only update the metadata cache for terms belonging to these posts if the term_id passed
+		 * to `get_term_meta()` matches one of those terms. This prevents a single call to
+		 * `get_term_meta()` from priming metadata for all `WP_Query` objects.
+		 */
+		if ( isset( $term_ids[ $term_id ] ) ) {
+			update_termmeta_cache( array_keys( $term_ids ) );
+		}
+
+		$this->updated_term_meta_cache[ $term_id ] = true;
+
+		return $check;
+	}
+}
\ No newline at end of file
