Use cached version listings as heuristic when prefetching (#2851)

diff --git a/lib/src/command/outdated.dart b/lib/src/command/outdated.dart
index 87e71b1..2b69c4c 100644
--- a/lib/src/command/outdated.dart
+++ b/lib/src/command/outdated.dart
@@ -8,10 +8,10 @@
 import 'dart:math';
 
 import 'package:meta/meta.dart';
-import 'package:pub/src/command_runner.dart';
 import 'package:pub_semver/pub_semver.dart';
 
 import '../command.dart';
+import '../command_runner.dart';
 import '../entrypoint.dart';
 import '../io.dart';
 import '../log.dart' as log;
diff --git a/lib/src/solver/version_solver.dart b/lib/src/solver/version_solver.dart
index 10f2126..f4ca367 100644
--- a/lib/src/solver/version_solver.dart
+++ b/lib/src/solver/version_solver.dart
@@ -6,7 +6,6 @@
 import 'dart:math' as math;
 
 import 'package:collection/collection.dart';
-import 'package:pub/src/source/hosted.dart';
 import 'package:pub_semver/pub_semver.dart';
 
 import '../exceptions.dart';
@@ -15,6 +14,7 @@
 import '../package.dart';
 import '../package_name.dart';
 import '../pubspec.dart';
+import '../source/hosted.dart';
 import '../source/unknown.dart';
 import '../system_cache.dart';
 import '../utils.dart';
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart
index d8c853c..3199a1d 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -206,7 +206,8 @@
     throw FormatException('versions must be a list');
   }
 
-  Future<Map<PackageId, _VersionInfo>> _fetchVersions(PackageRef ref) async {
+  Future<Map<PackageId, _VersionInfo>> _fetchVersionsNoPrefetching(
+      PackageRef ref) async {
     var url = _makeUrl(
         ref.description, (server, package) => '$server/api/packages/$package');
     log.io('Get versions from $url.');
@@ -230,20 +231,23 @@
     if (body.length < 100 * 1024) {
       await _cacheVersionListingResponse(body, ref);
     }
+    return result;
+  }
 
-    // Prefetch the dependencies of the latest version, we are likely to need
-    // them later.
+  Future<Map<PackageId, _VersionInfo>> _fetchVersions(PackageRef ref) async {
     final preschedule =
         Zone.current[_prefetchingKey] as void Function(PackageRef);
-    if (preschedule != null) {
-      final latestVersion =
-          maxBy(result.keys.map((id) => id.version), (e) => e);
 
+    /// Prefetch the dependencies of the latest version, we are likely to need
+    /// them later.
+    void prescheduleDependenciesOfLatest(Map<PackageId, _VersionInfo> listing) {
+      if (listing == null) return;
+      final latestVersion =
+          maxBy(listing.keys.map((id) => id.version), (e) => e);
       final latestVersionId =
           PackageId(ref.name, source, latestVersion, ref.description);
-
       final dependencies =
-          result[latestVersionId]?.pubspec?.dependencies?.values ?? [];
+          listing[latestVersionId]?.pubspec?.dependencies?.values ?? [];
       unawaited(withDependencyType(DependencyType.none, () async {
         for (final packageRange in dependencies) {
           if (packageRange.source is HostedSource) {
@@ -252,6 +256,21 @@
         }
       }));
     }
+
+    if (preschedule != null) {
+      /// If we have a cached response - preschedule dependencies of that.
+      prescheduleDependenciesOfLatest(
+        await _cachedVersionListingResponse(ref, Duration(days: 365)),
+      );
+    }
+    final result = await _fetchVersionsNoPrefetching(ref);
+
+    if (preschedule != null) {
+      // Preschedule the dependencies from the actual response.
+      // This might overlap with those from the cached response. But the
+      // scheduler ensures each listing will be fetched at most once.
+      prescheduleDependenciesOfLatest(result);
+    }
     return result;
   }
 
@@ -356,7 +375,7 @@
   /// site.
   @override
   Future<List<PackageId>> doGetVersions(PackageRef ref, Duration maxAge) async {
-    Map<PackageId, _VersionInfo> versionListing;
+    var versionListing = _scheduler.peek(ref);
     if (maxAge != null) {
       // Do we have a cached version response on disk?
       versionListing ??= await _cachedVersionListingResponse(ref, maxAge);