From bd5f259b3b60a5fc58b70025c8c6867dd40fa586 Mon Sep 17 00:00:00 2001
From: "floitsch@google.com" <floitsch@google.com>
Date: Mon, 7 Jan 2013 11:23:16 +0000
Subject: [PATCH] Big merge from experimental to bleeding edge.

Review URL: https://codereview.chromium.org//11783009

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge@16687 260f80e4-7a28-3924-810f-c04153c831b5
---
 lib/src/command_install.dart     |   4 +-
 lib/src/command_lish.dart        |  16 +--
 lib/src/command_update.dart      |   3 +-
 lib/src/curl_client.dart         |   9 +-
 lib/src/entrypoint.dart          |  75 +++++++-------
 lib/src/git.dart                 |  38 +++----
 lib/src/git_source.dart          |  31 +++---
 lib/src/hosted_source.dart       |  21 ++--
 lib/src/io.dart                  | 127 ++++++++++++------------
 lib/src/lock_file.dart           |   4 +-
 lib/src/log.dart                 |   2 +-
 lib/src/oauth2.dart              |   8 +-
 lib/src/package.dart             |   5 +-
 lib/src/pub.dart                 |  36 +++----
 lib/src/pubspec.dart             |   4 +-
 lib/src/sdk_source.dart          |  15 +--
 lib/src/source.dart              |  11 ++-
 lib/src/system_cache.dart        |   5 +-
 lib/src/utils.dart               |  37 ++++---
 lib/src/validator.dart           |  11 ++-
 lib/src/validator/directory.dart |   4 +-
 lib/src/validator/lib.dart       |   4 +-
 lib/src/validator/license.dart   |   4 +-
 lib/src/validator/name.dart      |   2 +-
 lib/src/version.dart             |  14 +--
 lib/src/version_solver.dart      |  36 +++----
 lib/src/yaml/composer.dart       |  10 +-
 lib/src/yaml/model.dart          |  10 +-
 lib/src/yaml/parser.dart         |   2 +-
 lib/src/yaml/visitor.dart        |   3 +-
 lib/src/yaml/yaml.dart           |   5 +-
 lib/src/yaml/yaml_map.dart       |   8 +-
 test/curl_client_test.dart       |  30 +++---
 test/oauth2_test.dart            |   6 +-
 test/pub_lish_test.dart          |   4 +-
 test/test_pub.dart               | 163 +++++++++++++++----------------
 test/version_solver_test.dart    |   7 +-
 test/yaml_test.dart              |   6 +-
 38 files changed, 394 insertions(+), 386 deletions(-)

diff --git a/lib/src/command_install.dart b/lib/src/command_install.dart
index 22637554..001ba21c 100644
--- a/lib/src/command_install.dart
+++ b/lib/src/command_install.dart
@@ -4,6 +4,8 @@
 
 library command_install;
 
+import 'dart:async';
+
 import 'entrypoint.dart';
 import 'log.dart' as log;
 import 'pub.dart';
@@ -14,7 +16,7 @@ class InstallCommand extends PubCommand {
   String get usage => "pub install";
 
   Future onRun() {
-    return entrypoint.installDependencies().transform((_) {
+    return entrypoint.installDependencies().then((_) {
       log.message("Dependencies installed!");
     });
   }
diff --git a/lib/src/command_lish.dart b/lib/src/command_lish.dart
index 5afe32ff..01136fda 100644
--- a/lib/src/command_lish.dart
+++ b/lib/src/command_lish.dart
@@ -63,12 +63,12 @@ class LishCommand extends PubCommand {
         request.files.add(new http.MultipartFile.fromBytes(
             'file', packageBytes, filename: 'package.tar.gz'));
         return client.send(request);
-      }).chain(http.Response.fromStream).transform((response) {
+      }).chain(http.Response.fromStream).then((response) {
         var location = response.headers['location'];
         if (location == null) throw new PubHttpException(response);
         return location;
-      }).chain((location) => client.get(location))
-          .transform(handleJsonSuccess);
+      }).then((location) => client.get(location))
+          .then(handleJsonSuccess);
     }).transformException((e) {
       if (e is! PubHttpException) throw e;
       var url = e.response.request.url;
@@ -126,8 +126,8 @@ class LishCommand extends PubCommand {
       }
 
       return listDir(rootDir, recursive: true).chain((entries) {
-        return Futures.wait(entries.map((entry) {
-          return fileExists(entry).transform((isFile) {
+        return Futures.wait(entries.mappedBy((entry) {
+          return fileExists(entry).then((isFile) {
             // Skip directories.
             if (!isFile) return null;
 
@@ -140,13 +140,13 @@ class LishCommand extends PubCommand {
           });
         }));
       });
-    }).transform((files) => files.filter((file) {
+    }).then((files) => files.where((file) {
       if (file == null || _BLACKLISTED_FILES.contains(basename(file))) {
         return false;
       }
 
       return !splitPath(file).some(_BLACKLISTED_DIRECTORIES.contains);
-    }));
+    }).toList());
   }
 
   /// Returns the value associated with [key] in [map]. Throws a user-friendly
@@ -176,7 +176,7 @@ class LishCommand extends PubCommand {
         message = "Package has ${warnings.length} warning$s. Upload anyway";
       }
 
-      return confirm(message).transform((confirmed) {
+      return confirm(message).then((confirmed) {
         if (!confirmed) throw "Package upload canceled.";
       });
     });
diff --git a/lib/src/command_update.dart b/lib/src/command_update.dart
index 1dda303e..4757049c 100644
--- a/lib/src/command_update.dart
+++ b/lib/src/command_update.dart
@@ -4,6 +4,7 @@
 
 library command_update;
 
+import 'dart:async';
 import 'entrypoint.dart';
 import 'log.dart' as log;
 import 'pub.dart';
@@ -22,6 +23,6 @@ class UpdateCommand extends PubCommand {
     } else {
       future = entrypoint.updateDependencies(commandOptions.rest);
     }
-    return future.transform((_) => log.message("Dependencies updated!"));
+    return future.then((_) => log.message("Dependencies updated!"));
   }
 }
diff --git a/lib/src/curl_client.dart b/lib/src/curl_client.dart
index 6922b828..7361b09a 100644
--- a/lib/src/curl_client.dart
+++ b/lib/src/curl_client.dart
@@ -4,6 +4,7 @@
 
 library curl_client;
 
+import 'dart:async';
 import 'dart:io';
 
 import '../../pkg/http/lib/http.dart' as http;
@@ -39,7 +40,7 @@ class CurlClient extends http.BaseClient {
       var arguments = _argumentsForRequest(request, headerFile);
       log.process(executable, arguments);
       var process;
-      return startProcess(executable, arguments).chain((process_) {
+      return startProcess(executable, arguments).then((process_) {
         process = process_;
         if (requestStream.closed) {
           process.stdin.close();
@@ -48,8 +49,8 @@ class CurlClient extends http.BaseClient {
         }
 
         return _waitForHeaders(process, expectBody: request.method != "HEAD");
-      }).chain((_) => new File(headerFile).readAsLines())
-        .transform((lines) => _buildResponse(request, process, lines));
+      }).then((_) => new File(headerFile).readAsLines())
+        .then((lines) => _buildResponse(request, process, lines));
     });
   }
 
@@ -126,7 +127,7 @@ class CurlClient extends http.BaseClient {
       }
 
       chainToCompleter(consumeInputStream(process.stderr)
-            .transform((stderrBytes) {
+            .then((stderrBytes) {
         var message = new String.fromCharCodes(stderrBytes);
         log.fine('Got error reading headers from curl: $message');
         if (exitCode == 47) {
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index b52a139e..457d480f 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -4,6 +4,7 @@
 
 library entrypoint;
 
+import 'dart:async';
 import 'io.dart';
 import 'lock_file.dart';
 import 'log.dart' as log;
@@ -46,7 +47,7 @@ class Entrypoint {
 
   /// Loads the entrypoint from a package at [rootDir].
   static Future<Entrypoint> load(String rootDir, SystemCache cache) {
-    return Package.load(null, rootDir, cache.sources).transform((package) =>
+    return Package.load(null, rootDir, cache.sources).then((package) =>
         new Entrypoint(package, cache));
   }
 
@@ -70,26 +71,26 @@ class Entrypoint {
     if (pendingOrCompleted != null) return pendingOrCompleted;
 
     var packageDir = join(path, id.name);
-    var future = ensureDir(dirname(packageDir)).chain((_) {
+    var future = ensureDir(dirname(packageDir)).then((_) {
       return exists(packageDir);
-    }).chain((exists) {
+    }).then((exists) {
       if (!exists) return new Future.immediate(null);
       // TODO(nweiz): figure out when to actually delete the directory, and when
       // we can just re-use the existing symlink.
       log.fine("Deleting package directory for ${id.name} before install.");
       return deleteDir(packageDir);
-    }).chain((_) {
+    }).then((_) {
       if (id.source.shouldCache) {
-        return cache.install(id).chain(
+        return cache.install(id).then(
             (pkg) => createPackageSymlink(id.name, pkg.dir, packageDir));
       } else {
-        return id.source.install(id, packageDir).transform((found) {
+        return id.source.install(id, packageDir).then((found) {
           if (found) return null;
           // TODO(nweiz): More robust error-handling.
           throw 'Package ${id.name} not found in source "${id.source.name}".';
         });
       }
-    }).chain((_) => id.resolved);
+    }).then((_) => id.resolved);
 
     _installs[id] = future;
 
@@ -101,8 +102,8 @@ class Entrypoint {
   /// completes when all dependencies are installed.
   Future installDependencies() {
     return loadLockFile()
-      .chain((lockFile) => resolveVersions(cache.sources, root, lockFile))
-      .chain(_installDependencies);
+      .then((lockFile) => resolveVersions(cache.sources, root, lockFile))
+      .then(_installDependencies);
   }
 
   /// Installs the latest available versions of all dependencies of the [root]
@@ -110,33 +111,33 @@ class Entrypoint {
   /// [Future] that completes when all dependencies are installed.
   Future updateAllDependencies() {
     return resolveVersions(cache.sources, root, new LockFile.empty())
-      .chain(_installDependencies);
+      .then(_installDependencies);
   }
 
   /// Installs the latest available versions of [dependencies], while leaving
   /// other dependencies as specified by the [LockFile] if possible. Returns a
   /// [Future] that completes when all dependencies are installed.
   Future updateDependencies(List<String> dependencies) {
-    return loadLockFile().chain((lockFile) {
+    return loadLockFile().then((lockFile) {
       var versionSolver = new VersionSolver(cache.sources, root, lockFile);
       for (var dependency in dependencies) {
         versionSolver.useLatestVersion(dependency);
       }
       return versionSolver.solve();
-    }).chain(_installDependencies);
+    }).then(_installDependencies);
   }
 
   /// Removes the old packages directory, installs all dependencies listed in
   /// [packageVersions], and writes a [LockFile].
   Future _installDependencies(List<PackageId> packageVersions) {
-    return cleanDir(path).chain((_) {
-      return Futures.wait(packageVersions.map((id) {
+    return cleanDir(path).then((_) {
+      return Futures.wait(packageVersions.mappedBy((id) {
         if (id.source is RootSource) return new Future.immediate(id);
         return install(id);
       }));
-    }).chain(_saveLockFile)
-      .chain(_installSelfReference)
-      .chain(_linkSecondaryPackageDirs);
+    }).then(_saveLockFile)
+      .then(_installSelfReference)
+      .then(_linkSecondaryPackageDirs);
   }
 
   /// Loads the list of concrete package versions from the `pubspec.lock`, if it
@@ -145,13 +146,13 @@ class Entrypoint {
     var lockFilePath = join(root.dir, 'pubspec.lock');
 
     log.fine("Loading lockfile.");
-    return fileExists(lockFilePath).chain((exists) {
+    return fileExists(lockFilePath).then((exists) {
       if (!exists) {
         log.fine("No lock file at $lockFilePath, creating empty one.");
         return new Future<LockFile>.immediate(new LockFile.empty());
       }
 
-      return readTextFile(lockFilePath).transform((text) =>
+      return readTextFile(lockFilePath).then((text) =>
           new LockFile.parse(text, cache.sources));
     });
   }
@@ -172,10 +173,10 @@ class Entrypoint {
   /// allow a package to import its own files using `package:`.
   Future _installSelfReference(_) {
     var linkPath = join(path, root.name);
-    return exists(linkPath).chain((exists) {
+    return exists(linkPath).then((exists) {
       // Create the symlink if it doesn't exist.
       if (exists) return new Future.immediate(null);
-      return ensureDir(path).chain(
+      return ensureDir(path).then(
           (_) => createPackageSymlink(root.name, root.dir, linkPath,
               isSelfLink: true));
     });
@@ -190,25 +191,25 @@ class Entrypoint {
     var testDir = join(root.dir, 'test');
     var toolDir = join(root.dir, 'tool');
     var webDir = join(root.dir, 'web');
-    return dirExists(binDir).chain((exists) {
+    return dirExists(binDir).then((exists) {
       if (!exists) return new Future.immediate(null);
       return _linkSecondaryPackageDir(binDir);
-    }).chain((_) => _linkSecondaryPackageDirsRecursively(exampleDir))
-      .chain((_) => _linkSecondaryPackageDirsRecursively(testDir))
-      .chain((_) => _linkSecondaryPackageDirsRecursively(toolDir))
-      .chain((_) => _linkSecondaryPackageDirsRecursively(webDir));
+    }).then((_) => _linkSecondaryPackageDirsRecursively(exampleDir))
+      .then((_) => _linkSecondaryPackageDirsRecursively(testDir))
+      .then((_) => _linkSecondaryPackageDirsRecursively(toolDir))
+      .then((_) => _linkSecondaryPackageDirsRecursively(webDir));
   }
 
   /// Creates a symlink to the `packages` directory in [dir] and all its
   /// subdirectories.
   Future _linkSecondaryPackageDirsRecursively(String dir) {
-    return dirExists(dir).chain((exists) {
+    return dirExists(dir).then((exists) {
       if (!exists) return new Future.immediate(null);
       return _linkSecondaryPackageDir(dir)
-        .chain((_) => _listDirWithoutPackages(dir))
-        .chain((files) {
-        return Futures.wait(files.map((file) {
-          return dirExists(file).chain((isDir) {
+        .then((_) => _listDirWithoutPackages(dir))
+        .then((files) {
+        return Futures.wait(files.mappedBy((file) {
+          return dirExists(file).then((isDir) {
             if (!isDir) return new Future.immediate(null);
             return _linkSecondaryPackageDir(file);
           });
@@ -221,25 +222,25 @@ class Entrypoint {
   /// Recursively lists the contents of [dir], excluding hidden `.DS_Store`
   /// files and `package` files.
   Future<List<String>> _listDirWithoutPackages(dir) {
-    return listDir(dir).chain((files) {
-      return Futures.wait(files.map((file) {
+    return listDir(dir).then((files) {
+      return Futures.wait(files.mappedBy((file) {
         if (basename(file) == 'packages') return new Future.immediate([]);
-        return dirExists(file).chain((isDir) {
+        return dirExists(file).then((isDir) {
           if (!isDir) return new Future.immediate([]);
           return _listDirWithoutPackages(file);
-        }).transform((subfiles) {
+        }).then((subfiles) {
           var fileAndSubfiles = [file];
           fileAndSubfiles.addAll(subfiles);
           return fileAndSubfiles;
         });
       }));
-    }).transform(flatten);
+    }).then(flatten);
   }
 
   /// Creates a symlink to the `packages` directory in [dir] if none exists.
   Future _linkSecondaryPackageDir(String dir) {
     var to = join(dir, 'packages');
-    return exists(to).chain((exists) {
+    return exists(to).then((exists) {
       if (exists) return new Future.immediate(null);
       return createSymlink(path, to);
     });
diff --git a/lib/src/git.dart b/lib/src/git.dart
index b216735f..df2e8565 100644
--- a/lib/src/git.dart
+++ b/lib/src/git.dart
@@ -5,6 +5,7 @@
 /// Helper functionality for invoking Git.
 library git;
 
+import 'dart:async';
 import 'io.dart';
 import 'log.dart' as log;
 import 'utils.dart';
@@ -14,18 +15,18 @@ Future<bool> get isInstalled {
   if (_isGitInstalledCache != null) {
     // TODO(rnystrom): The sleep is to pump the message queue. Can use
     // Future.immediate() when #3356 is fixed.
-    return sleep(0).transform((_) => _isGitInstalledCache);
+    return sleep(0).then((_) => _isGitInstalledCache);
   }
 
-  return _gitCommand.transform((git) => git != null);
+  return _gitCommand.then((git) => git != null);
 }
 
 /// Run a git process with [args] from [workingDir]. Returns the stdout as a
 /// list of strings if it succeeded. Completes to an exception if it failed.
 Future<List<String>> run(List<String> args, {String workingDir}) {
-  return _gitCommand.chain((git) {
+  return _gitCommand.then((git) {
     return runProcess(git, args, workingDir: workingDir);
-  }).transform((result) {
+  }).then((result) {
     if (!result.success) throw new Exception(
         'Git error. Command: git ${Strings.join(args, " ")}\n'
         '${Strings.join(result.stderr, "\n")}');
@@ -44,18 +45,18 @@ String _gitCommandCache;
 Future<String> get _gitCommand {
   // TODO(nweiz): Just use Future.immediate once issue 3356 is fixed.
   if (_gitCommandCache != null) {
-    return sleep(0).transform((_) => _gitCommandCache);
+    return sleep(0).then((_) => _gitCommandCache);
   }
 
-  return _tryGitCommand("git").chain((success) {
+  return _tryGitCommand("git").then((success) {
     if (success) return new Future.immediate("git");
 
     // Git is sometimes installed on Windows as `git.cmd`
-    return _tryGitCommand("git.cmd").transform((success) {
+    return _tryGitCommand("git.cmd").then((success) {
       if (success) return "git.cmd";
       return null;
     });
-  }).transform((command) {
+  }).then((command) {
     log.fine('Determined git command $command.');
     _gitCommandCache = command;
     return command;
@@ -69,17 +70,16 @@ Future<bool> _tryGitCommand(String command) {
   // If "git --version" prints something familiar, git is working.
   var future = runProcess(command, ["--version"]);
 
-  future.then((results) {
-    var regex = new RegExp("^git version");
-    completer.complete(results.stdout.length == 1 &&
-                       regex.hasMatch(results.stdout[0]));
-  });
-
-  future.handleException((err) {
-    // If the process failed, they probably don't have it.
-    completer.complete(false);
-    return true;
-  });
+  future
+    .then((results) {
+      var regex = new RegExp("^git version");
+      completer.complete(results.stdout.length == 1 &&
+                         regex.hasMatch(results.stdout[0]));
+    })
+    .catchError((err) {
+      // If the process failed, they probably don't have it.
+      completer.complete(false);
+    });
 
   return completer.future;
 }
diff --git a/lib/src/git_source.dart b/lib/src/git_source.dart
index e54b6cf1..0a3e2b97 100644
--- a/lib/src/git_source.dart
+++ b/lib/src/git_source.dart
@@ -4,6 +4,7 @@
 
 library git_source;
 
+import 'dart:async';
 import 'git.dart' as git;
 import 'io.dart';
 import 'package.dart';
@@ -34,7 +35,7 @@ class GitSource extends Source {
   Future<Package> installToSystemCache(PackageId id) {
     var revisionCachePath;
 
-    return git.isInstalled.chain((installed) {
+    return git.isInstalled.then((installed) {
       if (!installed) {
         throw new Exception(
             "Cannot install '${id.name}' from Git (${_getUrl(id)}).\n"
@@ -42,19 +43,19 @@ class GitSource extends Source {
       }
 
       return ensureDir(join(systemCacheRoot, 'cache'));
-    }).chain((_) => _ensureRepoCache(id))
-      .chain((_) => _revisionCachePath(id))
-      .chain((path) {
+    }).then((_) => _ensureRepoCache(id))
+      .then((_) => _revisionCachePath(id))
+      .then((path) {
       revisionCachePath = path;
       return exists(revisionCachePath);
-    }).chain((exists) {
+    }).then((exists) {
       if (exists) return new Future.immediate(null);
       return _clone(_repoCachePath(id), revisionCachePath, mirror: false);
-    }).chain((_) {
+    }).then((_) {
       var ref = _getEffectiveRef(id);
       if (ref == 'HEAD') return new Future.immediate(null);
       return _checkOut(revisionCachePath, ref);
-    }).chain((_) {
+    }).then((_) {
       return Package.load(id.name, revisionCachePath, systemCache.sources);
     });
   }
@@ -91,7 +92,7 @@ class GitSource extends Source {
 
   /// Attaches a specific commit to [id] to disambiguate it.
   Future<PackageId> resolveId(PackageId id) {
-    return _revisionAt(id).transform((revision) {
+    return _revisionAt(id).then((revision) {
       var description = {'url': _getUrl(id), 'ref': _getRef(id)};
       description['resolved-ref'] = revision;
       return new PackageId(id.name, this, id.version, description);
@@ -104,22 +105,22 @@ class GitSource extends Source {
   /// fails.
   Future _ensureRepoCache(PackageId id) {
     var path = _repoCachePath(id);
-    return exists(path).chain((exists) {
+    return exists(path).then((exists) {
       if (!exists) return _clone(_getUrl(id), path, mirror: true);
 
-      return git.run(["fetch"], workingDir: path).transform((result) => null);
+      return git.run(["fetch"], workingDir: path).then((result) => null);
     });
   }
 
   /// Returns a future that completes to the revision hash of [id].
   Future<String> _revisionAt(PackageId id) {
     return git.run(["rev-parse", _getEffectiveRef(id)],
-        workingDir: _repoCachePath(id)).transform((result) => result[0]);
+        workingDir: _repoCachePath(id)).then((result) => result[0]);
   }
 
   /// Returns the path to the revision-specific cache of [id].
   Future<String> _revisionCachePath(PackageId id) {
-    return _revisionAt(id).transform((rev) {
+    return _revisionAt(id).then((rev) {
       var revisionCacheName = '${id.name}-$rev';
       return join(systemCacheRoot, revisionCacheName);
     });
@@ -134,16 +135,16 @@ class GitSource extends Source {
   Future _clone(String from, String to, {bool mirror: false}) {
     // Git on Windows does not seem to automatically create the destination
     // directory.
-    return ensureDir(to).chain((_) {
+    return ensureDir(to).then((_) {
       var args = ["clone", from, to];
       if (mirror) args.insertRange(1, 1, "--mirror");
       return git.run(args);
-    }).transform((result) => null);
+    }).then((result) => null);
   }
 
   /// Checks out the reference [ref] in [repoPath].
   Future _checkOut(String repoPath, String ref) {
-    return git.run(["checkout", ref], workingDir: repoPath).transform(
+    return git.run(["checkout", ref], workingDir: repoPath).then(
         (result) => null);
   }
 
diff --git a/lib/src/hosted_source.dart b/lib/src/hosted_source.dart
index 4b515aca..45867e5f 100644
--- a/lib/src/hosted_source.dart
+++ b/lib/src/hosted_source.dart
@@ -4,8 +4,9 @@
 
 library hosted_source;
 
+import 'dart:async';
 import 'dart:io' as io;
-import 'dart:json';
+import 'dart:json' as json;
 import 'dart:uri';
 
 // TODO(nweiz): Make this import better.
@@ -35,9 +36,11 @@ class HostedSource extends Source {
     var parsed = _parseDescription(description);
     var fullUrl = "${parsed.last}/packages/${parsed.first}.json";
 
-    return httpClient.read(fullUrl).transform((body) {
-      var doc = JSON.parse(body);
-      return doc['versions'].map((version) => new Version.parse(version));
+    return httpClient.read(fullUrl).then((body) {
+      var doc = json.parse(body);
+      return doc['versions']
+          .mappedBy((version) => new Version.parse(version))
+          .toList();
     }).transformException((ex) {
       _throwFriendlyError(ex, parsed.first, parsed.last);
     });
@@ -50,7 +53,7 @@ class HostedSource extends Source {
     var fullUrl = "${parsed.last}/packages/${parsed.first}/versions/"
       "${id.version}.yaml";
 
-    return httpClient.read(fullUrl).transform((yaml) {
+    return httpClient.read(fullUrl).then((yaml) {
       return new Pubspec.parse(yaml, systemCache.sources);
     }).transformException((ex) {
       _throwFriendlyError(ex, id, parsed.last);
@@ -71,18 +74,18 @@ class HostedSource extends Source {
     var tempDir;
     return Futures.wait([
       httpClient.send(new http.Request("GET", new Uri.fromString(fullUrl)))
-          .transform((response) => response.stream),
+          .then((response) => response.stream),
       systemCache.createTempDir()
-    ]).chain((args) {
+    ]).then((args) {
       tempDir = args[1];
       return timeout(extractTarGz(args[0], tempDir), HTTP_TIMEOUT,
           'fetching URL "$fullUrl"');
-    }).chain((_) {
+    }).then((_) {
       // Now that the install has succeeded, move it to the real location in
       // the cache. This ensures that we don't leave half-busted ghost
       // directories in the user's pub cache if an install fails.
       return renameDir(tempDir, destPath);
-    }).transform((_) => true);
+    }).then((_) => true);
   }
 
   /// The system cache directory for the hosted source contains subdirectories
diff --git a/lib/src/io.dart b/lib/src/io.dart
index 8b53575a..cc8902c3 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -5,6 +5,7 @@
 /// Helper functionality to make working with IO easier.
 library io;
 
+import 'dart:async';
 import 'dart:io';
 import 'dart:isolate';
 import 'dart:json';
@@ -26,7 +27,7 @@ final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?");
 /// [File] objects.
 String join(part1, [part2, part3, part4, part5, part6, part7, part8]) {
   var parts = [part1, part2, part3, part4, part5, part6, part7, part8]
-      .map((part) => part == null ? null : _getPath(part));
+      .mappedBy((part) => part == null ? null : _getPath(part)).toList();
 
   return path.join(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5],
       parts[6], parts[7]);
@@ -58,7 +59,7 @@ String relativeTo(target, base) => path.relative(target, from: base);
 /// completes with the result.
 Future<bool> exists(path) {
   path = _getPath(path);
-  return Futures.wait([fileExists(path), dirExists(path)]).transform((results) {
+  return Futures.wait([fileExists(path), dirExists(path)]).then((results) {
     return results[0] || results[1];
   });
 }
@@ -103,9 +104,9 @@ Future<File> writeTextFile(file, String contents, {dontLogContents: false}) {
     log.fine("Contents:\n$contents");
   }
 
-  return file.open(FileMode.WRITE).chain((opened) {
-    return opened.writeString(contents).chain((ignore) {
-        return opened.close().transform((_) {
+  return file.open(FileMode.WRITE).then((opened) {
+    return opened.writeString(contents).then((ignore) {
+        return opened.close().then((_) {
           log.fine("Wrote text file $path.");
           return file;
         });
@@ -177,29 +178,25 @@ Future<Directory> ensureDir(path) {
   log.fine("Ensuring directory $path exists.");
   if (path == '.') return new Future.immediate(new Directory('.'));
 
-  return dirExists(path).chain((exists) {
+  return dirExists(path).then((exists) {
     if (exists) {
       log.fine("Directory $path already exists.");
       return new Future.immediate(new Directory(path));
     }
 
-    return ensureDir(dirname(path)).chain((_) {
-      var completer = new Completer<Directory>();
-      var future = createDir(path);
-      future.handleException((error) {
-        if (error is! DirectoryIOException) return false;
-        // Error 17 means the directory already exists (or 183 on Windows).
-        if (error.osError.errorCode != 17 &&
-            error.osError.errorCode != 183) {
+    return ensureDir(dirname(path)).then((_) {
+      return createDir(path)
+        .catchError((error) {
+          if (error is! DirectoryIOException) return false;
+          // Error 17 means the directory already exists (or 183 on Windows).
+          if (error.osError.errorCode != 17 &&
+              error.osError.errorCode != 183) {
           log.fine("Got 'already exists' error when creating directory.");
           return false;
         }
 
-        completer.complete(_getDirectory(path));
-        return true;
-      });
-      future.then(completer.complete);
-      return completer.future;
+          return _getDirectory(path);
+        });
     });
   });
 }
@@ -310,10 +307,10 @@ Future<bool> dirExists(dir) {
 /// new empty directory will be created. Returns a [Future] that completes when
 /// the new clean directory is created.
 Future<Directory> cleanDir(dir) {
-  return dirExists(dir).chain((exists) {
+  return dirExists(dir).then((exists) {
     if (exists) {
       // Delete it first.
-      return deleteDir(dir).chain((_) => createDir(dir));
+      return deleteDir(dir).then((_) => createDir(dir));
     } else {
       // Just create it.
       return createDir(dir);
@@ -327,7 +324,7 @@ Future<Directory> renameDir(from, String to) {
   from = _getDirectory(from);
   log.io("Renaming directory ${from.path} to $to.");
 
-  return _attemptRetryable(() => from.rename(to)).transform((dir) {
+  return _attemptRetryable(() => from.rename(to)).then((dir) {
     log.fine("Renamed directory ${from.path} to $to.");
     return dir;
   });
@@ -385,7 +382,7 @@ Future<File> createSymlink(from, to) {
     args = ['/j', to, from];
   }
 
-  return runProcess(command, args).transform((result) {
+  return runProcess(command, args).then((result) {
     // TODO(rnystrom): Check exit code and output?
     return new File(to);
   });
@@ -400,7 +397,7 @@ Future<File> createPackageSymlink(String name, from, to,
     {bool isSelfLink: false}) {
   // See if the package has a "lib" directory.
   from = join(from, 'lib');
-  return dirExists(from).chain((exists) {
+  return dirExists(from).then((exists) {
     log.fine("Creating ${isSelfLink ? "self" : ""}link for package '$name'.");
     if (exists) return createSymlink(from, to);
 
@@ -582,7 +579,7 @@ InputStream wrapInputStream(InputStream source) {
 Future<PubProcessResult> runProcess(String executable, List<String> args,
     {workingDir, Map<String, String> environment}) {
   return _doProcess(Process.run, executable, args, workingDir, environment)
-      .transform((result) {
+      .then((result) {
     // TODO(rnystrom): Remove this and change to returning one string.
     List<String> toLines(String output) {
       var lines = output.split(NEWLINE_PATTERN);
@@ -608,7 +605,7 @@ Future<PubProcessResult> runProcess(String executable, List<String> args,
 Future<Process> startProcess(String executable, List<String> args,
     {workingDir, Map<String, String> environment}) =>
   _doProcess(Process.start, executable, args, workingDir, environment)
-    .transform((process) => new _WrappedProcess(process));
+    .then((process) => new _WrappedProcess(process));
 
 /// A wrapper around [Process] that buffers the stdout and stderr to avoid
 /// running into issue 7218.
@@ -671,23 +668,24 @@ Future _doProcess(Function fn, String executable, List<String> args, workingDir,
 /// Note that timing out will not cancel the asynchronous operation behind
 /// [input].
 Future timeout(Future input, int milliseconds, String description) {
+  bool completed = false;
   var completer = new Completer();
   var timer = new Timer(milliseconds, (_) {
-    if (completer.future.isComplete) return;
-    completer.completeException(new TimeoutException(
+    completer = true;
+    completer.completeError(new TimeoutException(
         'Timed out while $description.'));
   });
-  input.handleException((e) {
-    if (completer.future.isComplete) return false;
-    timer.cancel();
-    completer.completeException(e, input.stackTrace);
-    return true;
-  });
-  input.then((value) {
-    if (completer.future.isComplete) return;
-    timer.cancel();
-    completer.complete(value);
-  });
+  input
+    .then((value) {
+      if (completed) return;
+      timer.cancel();
+      completer.complete(value);
+    })
+    .catchError((e) {
+      if (completed) return;
+      timer.cancel();
+      completer.completeError(e.error, e.stackTrace);
+    });
   return completer.future;
 }
 
@@ -696,11 +694,11 @@ Future timeout(Future input, int milliseconds, String description) {
 /// will be deleted.
 Future withTempDir(Future fn(String path)) {
   var tempDir;
-  var future = createTempDir().chain((dir) {
+  var future = createTempDir().then((dir) {
     tempDir = dir;
     return fn(tempDir.path);
   });
-  future.onComplete((_) {
+  future.catchError((_) {}).then(_) {
     log.fine('Cleaning up temp directory ${tempDir.path}.');
     deleteDir(tempDir);
   });
@@ -712,16 +710,16 @@ Future<bool> get isGitInstalled {
   if (_isGitInstalledCache != null) {
     // TODO(rnystrom): The sleep is to pump the message queue. Can use
     // Future.immediate() when #3356 is fixed.
-    return sleep(0).transform((_) => _isGitInstalledCache);
+    return sleep(0).then((_) => _isGitInstalledCache);
   }
 
-  return _gitCommand.transform((git) => git != null);
+  return _gitCommand.then((git) => git != null);
 }
 
 /// Run a git process with [args] from [workingDir].
 Future<PubProcessResult> runGit(List<String> args,
     {String workingDir, Map<String, String> environment}) {
-  return _gitCommand.chain((git) => runProcess(git, args,
+  return _gitCommand.then((git) => runProcess(git, args,
         workingDir: workingDir, environment: environment));
 }
 
@@ -730,18 +728,18 @@ Future<PubProcessResult> runGit(List<String> args,
 Future<String> get _gitCommand {
   // TODO(nweiz): Just use Future.immediate once issue 3356 is fixed.
   if (_gitCommandCache != null) {
-    return sleep(0).transform((_) => _gitCommandCache);
+    return sleep(0).then((_) => _gitCommandCache);
   }
 
-  return _tryGitCommand("git").chain((success) {
+  return _tryGitCommand("git").then((success) {
     if (success) return new Future.immediate("git");
 
     // Git is sometimes installed on Windows as `git.cmd`
-    return _tryGitCommand("git.cmd").transform((success) {
+    return _tryGitCommand("git.cmd").then((success) {
       if (success) return "git.cmd";
       return null;
     });
-  }).transform((command) {
+  }).then((command) {
     _gitCommandCache = command;
     return command;
   });
@@ -758,12 +756,9 @@ Future<bool> _tryGitCommand(String command) {
     var regex = new RegExp("^git version");
     completer.complete(results.stdout.length == 1 &&
                        regex.hasMatch(results.stdout[0]));
-  });
-
-  future.handleException((err) {
+  }).catchError((err) {
     // If the process failed, they probably don't have it.
     completer.complete(false);
-    return true;
   });
 
   return completer.future;
@@ -788,13 +783,11 @@ Future<bool> extractTarGz(InputStream stream, destination) {
     stream.pipe(process.stdin);
     process.stdout.pipe(stdout, close: false);
     process.stderr.pipe(stderr, close: false);
-  });
-  processFuture.handleException((error) {
-    completer.completeException(error, processFuture.stackTrace);
-    return true;
+  }).catchError((e) {
+    completer.completeError(e.error, e.stackTrace);
   });
 
-  return completer.future.transform((exitCode) {
+  return completer.future.then((exitCode) {
     log.fine("Extracted .tar.gz stream to $destination. Exit code $exitCode.");
     // TODO(rnystrom): Does anything check this result value? If not, it should
     // throw on a bad exit code.
@@ -818,17 +811,17 @@ Future<bool> _extractTarGzWindows(InputStream stream, String destination) {
   var tempDir;
 
   // TODO(rnystrom): Use withTempDir().
-  return createTempDir().chain((temp) {
+  return createTempDir().then((temp) {
     // Write the archive to a temp file.
     tempDir = temp;
     return createFileFromStream(stream, join(tempDir, 'data.tar.gz'));
-  }).chain((_) {
+  }).then((_) {
     // 7zip can't unarchive from gzip -> tar -> destination all in one step
     // first we un-gzip it to a tar file.
     // Note: Setting the working directory instead of passing in a full file
     // path because 7zip says "A full path is not allowed here."
     return runProcess(command, ['e', 'data.tar.gz'], workingDir: tempDir);
-  }).chain((result) {
+  }).then((result) {
     if (result.exitCode != 0) {
       throw 'Could not un-gzip (exit code ${result.exitCode}). Error:\n'
           '${Strings.join(result.stdout, "\n")}\n'
@@ -836,7 +829,7 @@ Future<bool> _extractTarGzWindows(InputStream stream, String destination) {
     }
     // Find the tar file we just created since we don't know its name.
     return listDir(tempDir);
-  }).chain((files) {
+  }).then((files) {
     var tarFile;
     for (var file in files) {
       if (path.extension(file) == '.tar') {
@@ -849,7 +842,7 @@ Future<bool> _extractTarGzWindows(InputStream stream, String destination) {
 
     // Untar the archive into the destination directory.
     return runProcess(command, ['x', tarFile], workingDir: destination);
-  }).chain((result) {
+  }).then((result) {
     if (result.exitCode != 0) {
       throw 'Could not un-tar (exit code ${result.exitCode}). Error:\n'
           '${Strings.join(result.stdout, "\n")}\n'
@@ -859,7 +852,7 @@ Future<bool> _extractTarGzWindows(InputStream stream, String destination) {
     log.fine('Clean up 7zip temp directory ${tempDir.path}.');
     // TODO(rnystrom): Should also delete this if anything fails.
     return deleteDir(tempDir);
-  }).transform((_) => true);
+  }).then((_) => true);
 }
 
 /// Create a .tar.gz archive from a list of entries. Each entry can be a
@@ -878,17 +871,17 @@ InputStream createTarGz(List contents, {baseDir}) {
 
   if (baseDir == null) baseDir = path.current;
   baseDir = getFullPath(baseDir);
-  contents = contents.map((entry) {
+  contents = contents.mappedBy((entry) {
     entry = getFullPath(entry);
     if (!isBeneath(entry, baseDir)) {
       throw 'Entry $entry is not inside $baseDir.';
     }
     return relativeTo(entry, baseDir);
-  });
+  }).toList();
 
   if (Platform.operatingSystem != "windows") {
     var args = ["--create", "--gzip", "--directory", baseDir];
-    args.addAll(contents.map(_getPath));
+    args.addAll(contents.mappedBy(_getPath));
     // TODO(nweiz): It's possible that enough command-line arguments will make
     // the process choke, so at some point we should save the arguments to a
     // file and pass them in via --files-from for tar and -i@filename for 7zip.
@@ -908,7 +901,7 @@ InputStream createTarGz(List contents, {baseDir}) {
     // Create the tar file.
     var tarFile = join(tempDir, "intermediate.tar");
     var args = ["a", "-w$baseDir", tarFile];
-    args.addAll(contents.map((entry) => '-i!"$entry"'));
+    args.addAll(contents.mappedBy((entry) => '-i!"$entry"'));
 
     // Note: This line of code gets munged by create_sdk.py to be the correct
     // relative path to 7zip in the SDK.
diff --git a/lib/src/lock_file.dart b/lib/src/lock_file.dart
index 80036b84..1e975852 100644
--- a/lib/src/lock_file.dart
+++ b/lib/src/lock_file.dart
@@ -4,7 +4,7 @@
 
 library lock_file;
 
-import 'dart:json';
+import 'dart:json' as json;
 import 'package.dart';
 import 'source_registry.dart';
 import 'utils.dart';
@@ -85,6 +85,6 @@ class LockFile {
 
     // TODO(nweiz): Serialize using the YAML library once it supports
     // serialization. For now, we use JSON, since it's a subset of YAML anyway.
-    return JSON.stringify({'packages': packagesObj});
+    return json.stringify({'packages': packagesObj});
   }
 }
diff --git a/lib/src/log.dart b/lib/src/log.dart
index 029f72cf..683c4a26 100644
--- a/lib/src/log.dart
+++ b/lib/src/log.dart
@@ -97,7 +97,7 @@ Future ioAsync(String startMessage, Future operation,
     io(startMessage);
   }
 
-  return operation.transform((result) {
+  return operation.then((result) {
     if (endMessage == null) {
       io("End $startMessage.");
     } else {
diff --git a/lib/src/oauth2.dart b/lib/src/oauth2.dart
index 378614b3..786ff35e 100644
--- a/lib/src/oauth2.dart
+++ b/lib/src/oauth2.dart
@@ -108,7 +108,7 @@ Future<Client> _getClient(SystemCache cache) {
     return new Future.immediate(new Client(
         _identifier, _secret, credentials, httpClient: curlClient));
   }).chain((client) {
-    return _saveCredentials(cache, client.credentials).transform((_) => client);
+    return _saveCredentials(cache, client.credentials).then((_) => client);
   });
 }
 
@@ -130,7 +130,7 @@ Future<Credentials> _loadCredentials(SystemCache cache) {
       return new Future.immediate(null);
     }
 
-    return readTextFile(_credentialsFile(cache)).transform((credentialsJson) {
+    return readTextFile(_credentialsFile(cache)).then((credentialsJson) {
       var credentials = new Credentials.fromJson(credentialsJson);
       if (credentials.isExpired && !credentials.canRefresh) {
         log.error("Pub's authorization to upload packages has expired and "
@@ -194,7 +194,7 @@ Future<Client> _authorize() {
       response.headers.set('location', 'http://pub.dartlang.org/authorized');
       response.outputStream.close();
       return grant.handleAuthorizationResponse(queryToMap(queryString));
-    }).transform((client) {
+    }).then((client) {
       server.close();
       return client;
     }), completer);
@@ -210,7 +210,7 @@ Future<Client> _authorize() {
       'Then click "Allow access".\n\n'
       'Waiting for your authorization...');
 
-  return completer.future.transform((client) {
+  return completer.future.then((client) {
     log.message('Successfully authorized.\n');
     return client;
   });
diff --git a/lib/src/package.dart b/lib/src/package.dart
index 69b54834..70f469c0 100644
--- a/lib/src/package.dart
+++ b/lib/src/package.dart
@@ -4,6 +4,7 @@
 
 library package;
 
+import 'dart:async';
 import 'io.dart';
 import 'pubspec.dart';
 import 'source.dart';
@@ -19,10 +20,10 @@ class Package {
       SourceRegistry sources) {
     var pubspecPath = join(packageDir, 'pubspec.yaml');
 
-    return fileExists(pubspecPath).chain((exists) {
+    return fileExists(pubspecPath).then((exists) {
       if (!exists) throw new PubspecNotFoundException(name);
       return readTextFile(pubspecPath);
-    }).transform((contents) {
+    }).then((contents) {
       try {
         var pubspec = new Pubspec.parse(contents, sources);
 
diff --git a/lib/src/pub.dart b/lib/src/pub.dart
index 6fcecd95..1a8642e7 100644
--- a/lib/src/pub.dart
+++ b/lib/src/pub.dart
@@ -5,6 +5,7 @@
 /// The main entrypoint for the pub command line application.
 library pub;
 
+import 'dart:async';
 import '../../pkg/args/lib/args.dart';
 import '../../pkg/path/lib/path.dart' as path;
 import 'dart:io';
@@ -244,7 +245,7 @@ abstract class PubCommand {
       future = Entrypoint.load(path.current, cache);
     }
 
-    future = future.chain((entrypoint) {
+    future = future.then((entrypoint) {
       this.entrypoint = entrypoint;
       try {
         var commandFuture = onRun();
@@ -257,23 +258,24 @@ abstract class PubCommand {
       }
     });
 
-    future = future.chain((_) => cache_.deleteTempDir());
 
-    future.handleException((e) {
-      if (e is PubspecNotFoundException && e.name == null) {
-        e = 'Could not find a file named "pubspec.yaml" in the directory '
-          '${path.current}.';
-      } else if (e is PubspecHasNoNameException && e.name == null) {
-        e = 'pubspec.yaml is missing the required "name" field (e.g. "name: '
-          '${basename(path.current)}").';
-      }
-
-      handleError(e, future.stackTrace);
-    });
-
-    // Explicitly exit on success to ensure that any dangling dart:io handles
-    // don't cause the process to never terminate.
-    future.then((_) => exit(0));
+    future
+      .then((_) => cache_.deleteTempDir())
+      .catchError((error) {
+        var e = error.error;
+        if (e is PubspecNotFoundException && e.name == null) {
+          e = 'Could not find a file named "pubspec.yaml" in the directory '
+            '${path.current}.';
+        } else if (e is PubspecHasNoNameException && e.name == null) {
+          e = 'pubspec.yaml is missing the required "name" field (e.g. "name: '
+            '${basename(path.current)}").';
+        }
+
+        handleError(e, error.stackTrace);
+      })
+      // Explicitly exit on success to ensure that any dangling dart:io handles
+      // don't cause the process to never terminate.
+      .then((_) => exit(0));
   }
 
   /// Override this to perform the specific command. Return a future that
diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart
index 8a1931dc..078b7e05 100644
--- a/lib/src/pubspec.dart
+++ b/lib/src/pubspec.dart
@@ -133,7 +133,7 @@ List<PackageRef> _parseDependencies(SourceRegistry sources, yaml) {
   // Allow an empty dependencies key.
   if (yaml == null) return dependencies;
 
-  if (yaml is! Map || yaml.keys.some((e) => e is! String)) {
+  if (yaml is! Map || yaml.keys.any((e) => e is! String)) {
     throw new FormatException(
         'The pubspec dependencies should be a map of package names, but '
         'was ${yaml}.');
@@ -154,7 +154,7 @@ List<PackageRef> _parseDependencies(SourceRegistry sources, yaml) {
         versionConstraint = new VersionConstraint.parse(spec.remove('version'));
       }
 
-      var sourceNames = spec.keys;
+      var sourceNames = spec.keys.toList();
       if (sourceNames.length > 1) {
         throw new FormatException(
             'Dependency $name may only have one source: $sourceNames.');
diff --git a/lib/src/sdk_source.dart b/lib/src/sdk_source.dart
index 654043a2..13ffa252 100644
--- a/lib/src/sdk_source.dart
+++ b/lib/src/sdk_source.dart
@@ -4,6 +4,7 @@
 
 library sdk_source;
 
+import 'dart:async';
 import 'io.dart';
 import 'package.dart';
 import 'pubspec.dart';
@@ -30,14 +31,14 @@ class SdkSource extends Source {
   /// inferred from the revision number of the SDK itself.
   Future<Pubspec> describe(PackageId id) {
     var version;
-    return readTextFile(join(rootDir, "revision")).chain((revision) {
+    return readTextFile(join(rootDir, "revision")).then((revision) {
       version = new Version.parse("0.0.0-r.${revision.trim()}");
       // Read the pubspec for the package's dependencies.
       return _getPackagePath(id);
-    }).chain((packageDir) {
+    }).then((packageDir) {
       // TODO(rnystrom): What if packageDir is null?
       return Package.load(id.name, packageDir, systemCache.sources);
-    }).transform((package) {
+    }).then((package) {
       // Ignore the pubspec's version, and use the SDK's.
       return new Pubspec(id.name, version, package.pubspec.dependencies);
     });
@@ -46,10 +47,10 @@ class SdkSource extends Source {
   /// Since all the SDK files are already available locally, installation just
   /// involves symlinking the SDK library into the packages directory.
   Future<bool> install(PackageId id, String destPath) {
-    return _getPackagePath(id).chain((path) {
+    return _getPackagePath(id).then((path) {
       if (path == null) return new Future<bool>.immediate(false);
 
-      return createPackageSymlink(id.name, path, destPath).transform(
+      return createPackageSymlink(id.name, path, destPath).then(
           (_) => true);
     });
   }
@@ -60,14 +61,14 @@ class SdkSource extends Source {
   Future<String> _getPackagePath(PackageId id) {
     // Look in "pkg" first.
     var pkgPath = join(rootDir, "pkg", id.description);
-    return exists(pkgPath).chain((found) {
+    return exists(pkgPath).then((found) {
       if (found) return new Future<String>.immediate(pkgPath);
 
       // Not in "pkg", so try "lib".
       // TODO(rnystrom): Get rid of this when all SDK packages are moved from
       // "lib" to "pkg".
       var libPath = join(rootDir, "lib", id.description);
-      return exists(libPath).transform((found) => found ? libPath : null);
+      return exists(libPath).then((found) => found ? libPath : null);
     });
   }
 }
diff --git a/lib/src/source.dart b/lib/src/source.dart
index 637cdd69..211f0dba 100644
--- a/lib/src/source.dart
+++ b/lib/src/source.dart
@@ -4,6 +4,7 @@
 
 library source;
 
+import 'dart:async';
 import 'io.dart';
 import 'package.dart';
 import 'pubspec.dart';
@@ -64,7 +65,7 @@ abstract class Source {
   /// uses [describe] to get that version.
   Future<List<Version>> getVersions(String name, description) {
     return describe(new PackageId(name, this, Version.none, description))
-      .transform((pubspec) => [pubspec.version]);
+      .then((pubspec) => [pubspec.version]);
   }
 
   /// Loads the (possibly remote) pubspec for the package version identified by
@@ -76,7 +77,7 @@ abstract class Source {
   /// must implement it manually.
   Future<Pubspec> describe(PackageId id) {
     if (!shouldCache) throw "Source $name must implement describe(id).";
-    return installToSystemCache(id).transform((package) => package.pubspec);
+    return installToSystemCache(id).then((package) => package.pubspec);
   }
 
   /// Installs the package identified by [id] to [path]. Returns a [Future] that
@@ -104,10 +105,10 @@ abstract class Source {
   /// By default, this uses [systemCacheDirectory] and [install].
   Future<Package> installToSystemCache(PackageId id) {
     var path = systemCacheDirectory(id);
-    return exists(path).chain((exists) {
+    return exists(path).then((exists) {
       if (exists) return new Future<bool>.immediate(true);
-      return ensureDir(dirname(path)).chain((_) => install(id, path));
-    }).chain((found) {
+      return ensureDir(dirname(path)).then((_) => install(id, path));
+    }).then((found) {
       if (!found) throw 'Package $id not found.';
       return Package.load(id.name, path, systemCache.sources);
     });
diff --git a/lib/src/system_cache.dart b/lib/src/system_cache.dart
index abd23482..5b00dc30 100644
--- a/lib/src/system_cache.dart
+++ b/lib/src/system_cache.dart
@@ -5,6 +5,7 @@
 library system_cache;
 
 import 'dart:io';
+import 'dart:async';
 
 import 'git_source.dart';
 import 'hosted_source.dart';
@@ -83,7 +84,7 @@ class SystemCache {
   /// temp directory to ensure that it's on the same volume as the pub system
   /// cache so that it can move the directory from it.
   Future<Directory> createTempDir() {
-    return ensureDir(tempDir).chain((temp) {
+    return ensureDir(tempDir).then((temp) {
       return io.createTempDir(join(temp, 'dir'));
     });
   }
@@ -91,7 +92,7 @@ class SystemCache {
   /// Delete's the system cache's internal temp directory.
   Future deleteTempDir() {
     log.fine('Clean up system cache temp directory $tempDir.');
-    return dirExists(tempDir).chain((exists) {
+    return dirExists(tempDir).then((exists) {
       if (!exists) return new Future.immediate(null);
       return deleteDir(tempDir);
     });
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 557e67de..b922d845 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -5,6 +5,7 @@
 /// Generic utility functions. Stuff that should possibly be in core.
 library utils;
 
+import 'dart:async';
 import 'dart:crypto';
 import 'dart:isolate';
 import 'dart:uri';
@@ -42,17 +43,12 @@ String padRight(String source, int length) {
 /// Runs [fn] after [future] completes, whether it completes successfully or
 /// not. Essentially an asynchronous `finally` block.
 always(Future future, fn()) {
-  var completer = new Completer();
-  future.then((_) => fn());
-  future.handleException((_) {
-    fn();
-    return false;
-  });
+  future.catchError((_) {}).then((_) => fn());
 }
 
-/// Flattens nested collections into a single list containing only non-list
-/// elements.
-List flatten(Collection nested) {
+/// Flattens nested lists inside an iterable into a single list containing only
+/// non-list elements.
+List flatten(Iterable nested) {
   var result = [];
   helper(list) {
     for (var element in list) {
@@ -69,10 +65,11 @@ List flatten(Collection nested) {
 
 /// Asserts that [iter] contains only one element, and returns it.
 only(Iterable iter) {
-  var iterator = iter.iterator();
-  assert(iterator.hasNext);
-  var obj = iterator.next();
-  assert(!iterator.hasNext);
+  var iterator = iter.iterator;
+  var currentIsValid = iterator.moveNext();
+  assert(currentIsValid);
+  var obj = iterator.current;
+  assert(!iterator.moveNext());
   return obj;
 }
 
@@ -108,7 +105,7 @@ bool endsWithPattern(String str, Pattern matcher) {
 
 /// Returns the hex-encoded sha1 hash of [source].
 String sha1(String source) =>
-  CryptoUtils.bytesToHex(new SHA1().update(source.charCodes).digest());
+  CryptoUtils.bytesToHex(new SHA1().add(source.charCodes).close());
 
 /// Returns a [Future] that completes in [milliseconds].
 Future sleep(int milliseconds) {
@@ -120,11 +117,11 @@ Future sleep(int milliseconds) {
 /// Configures [future] so that its result (success or exception) is passed on
 /// to [completer].
 void chainToCompleter(Future future, Completer completer) {
-  future.handleException((e) {
-    completer.completeException(e, future.stackTrace);
-    return true;
-  });
-  future.then(completer.complete);
+  future
+    .then(completer.complete)
+    .catchError((e) {
+      completer.completeError(e.error, e.stackTrace);
+    });
 }
 
 // TODO(nweiz): unify the following functions with the utility functions in
@@ -171,7 +168,7 @@ String mapToQuery(Map<String, String> map) {
     value = (value == null || value.isEmpty) ? null : encodeUriComponent(value);
     pairs.add([key, value]);
   });
-  return Strings.join(pairs.map((pair) {
+  return Strings.join(pairs.mappedBy((pair) {
     if (pair[1] == null) return pair[0];
     return "${pair[0]}=${pair[1]}";
   }), "&");
diff --git a/lib/src/validator.dart b/lib/src/validator.dart
index 63a2386c..70f5a9ff 100644
--- a/lib/src/validator.dart
+++ b/lib/src/validator.dart
@@ -55,10 +55,13 @@ abstract class Validator {
     // 3356, which causes a bug if all validators are (synchronously) using
     // Future.immediate and an error is thrown before a handler is set up.
     return sleep(0).chain((_) {
-      return Futures.wait(validators.map((validator) => validator.validate()));
-    }).transform((_) {
-      var errors = flatten(validators.map((validator) => validator.errors));
-      var warnings = flatten(validators.map((validator) => validator.warnings));
+      return Futures.wait(
+          validators.mappedBy((validator) => validator.validate()));
+    }).then((_) {
+      var errors =
+          flatten(validators.mappedBy((validator) => validator.errors));
+      var warnings =
+          flatten(validators.mappedBy((validator) => validator.warnings));
 
       if (!errors.isEmpty) {
         log.error("Missing requirements:");
diff --git a/lib/src/validator/directory.dart b/lib/src/validator/directory.dart
index 07ad933c..80568bab 100644
--- a/lib/src/validator/directory.dart
+++ b/lib/src/validator/directory.dart
@@ -17,8 +17,8 @@ class DirectoryValidator extends Validator {
 
   Future validate() {
     return listDir(entrypoint.root.dir).chain((dirs) {
-      return Futures.wait(dirs.map((dir) {
-        return dirExists(dir).transform((exists) {
+      return Futures.wait(dirs.mappedBy((dir) {
+        return dirExists(dir).then((exists) {
           if (!exists) return;
 
           dir = basename(dir);
diff --git a/lib/src/validator/lib.dart b/lib/src/validator/lib.dart
index b0dfbe5e..6afeb88f 100644
--- a/lib/src/validator/lib.dart
+++ b/lib/src/validator/lib.dart
@@ -29,8 +29,8 @@ class LibValidator extends Validator {
         return new Future.immediate(null);
       }
 
-      return listDir(libDir).transform((files) {
-        files = files.map((file) => relativeTo(file, libDir));
+      return listDir(libDir).then((files) {
+        files = files.mappedBy((file) => relativeTo(file, libDir)).toList();
         if (files.isEmpty) {
           errors.add('You must have a non-empty "lib" directory.\n'
               "Without that, users cannot import any code from your package.");
diff --git a/lib/src/validator/license.dart b/lib/src/validator/license.dart
index fcc6a49c..066ffbba 100644
--- a/lib/src/validator/license.dart
+++ b/lib/src/validator/license.dart
@@ -15,10 +15,10 @@ class LicenseValidator extends Validator {
     : super(entrypoint);
 
   Future validate() {
-    return listDir(entrypoint.root.dir).transform((files) {
+    return listDir(entrypoint.root.dir).then((files) {
       var licenseLike = new RegExp(
           r"^([a-zA-Z0-9]+[-_])?(LICENSE|COPYING)(\..*)?$");
-      if (files.map(basename).some(licenseLike.hasMatch)) return;
+      if (files.mappedBy(basename).any(licenseLike.hasMatch)) return;
 
       errors.add(
           "You must have a COPYING or LICENSE file in the root directory.\n"
diff --git a/lib/src/validator/name.dart b/lib/src/validator/name.dart
index 3d467c57..8d9e93df 100644
--- a/lib/src/validator/name.dart
+++ b/lib/src/validator/name.dart
@@ -49,7 +49,7 @@ class NameValidator extends Validator {
     return dirExists(libDir).chain((libDirExists) {
       if (!libDirExists) return new Future.immediate([]);
       return listDir(libDir, recursive: true);
-    }).transform((files) {
+    }).then((files) {
       return files.map((file) => relativeTo(file, dirname(libDir)))
           .filter((file) {
         return !splitPath(file).contains("src") &&
diff --git a/lib/src/version.dart b/lib/src/version.dart
index f60ad19c..e7ca3d12 100644
--- a/lib/src/version.dart
+++ b/lib/src/version.dart
@@ -57,9 +57,9 @@ class Version implements Comparable, VersionConstraint {
     }
 
     try {
-      int major = parseInt(match[1]);
-      int minor = parseInt(match[2]);
-      int patch = parseInt(match[3]);
+      int major = int.parse(match[1]);
+      int minor = int.parse(match[2]);
+      int patch = int.parse(match[3]);
 
       String preRelease = match[5];
       String build = match[8];
@@ -190,14 +190,14 @@ class Version implements Comparable, VersionConstraint {
   /// Splits a string of dot-delimited identifiers into their component parts.
   /// Identifiers that are numeric are converted to numbers.
   List _splitParts(String text) {
-    return text.split('.').map((part) {
+    return text.split('.').mappedBy((part) {
       try {
-        return parseInt(part);
+        return int.parse(part);
       } on FormatException catch (ex) {
         // Not a number.
         return part;
       }
-    });
+    }).toList();
   }
 }
 
@@ -241,7 +241,7 @@ abstract class VersionConstraint {
   /// allow. If constraints is empty, then it returns a VersionConstraint that
   /// allows all versions.
   factory VersionConstraint.intersection(
-      Collection<VersionConstraint> constraints) {
+      Iterable<VersionConstraint> constraints) {
     var constraint = new VersionRange();
     for (var other in constraints) {
       constraint = constraint.intersect(other);
diff --git a/lib/src/version_solver.dart b/lib/src/version_solver.dart
index a4b70f89..f277169f 100644
--- a/lib/src/version_solver.dart
+++ b/lib/src/version_solver.dart
@@ -35,7 +35,8 @@
 /// the beginning again.
 library version_solver;
 
-import 'dart:json';
+import 'dart:async';
+import 'dart:json' as json;
 import 'dart:math';
 import 'lock_file.dart';
 import 'log.dart' as log;
@@ -116,7 +117,7 @@ class VersionSolver {
         // If we have an async operation to perform, chain the loop to resume
         // when it's done. Otherwise, just loop synchronously.
         if (future != null) {
-          return future.chain(processNextWorkItem);
+          return future.then(processNextWorkItem);
         }
       }
     }
@@ -143,7 +144,7 @@ class VersionSolver {
   /// Returns the most recent version of [dependency] that satisfies all of its
   /// version constraints.
   Future<Version> getBestVersion(Dependency dependency) {
-    return dependency.getVersions().transform((versions) {
+    return dependency.getVersions().then((versions) {
       var best = null;
       for (var version in versions) {
         if (dependency.useLatestVersion ||
@@ -189,12 +190,12 @@ class VersionSolver {
       }
     }
 
-    return dependency.dependers.map(getDependency).some((subdependency) =>
+    return dependency.dependers.mappedBy(getDependency).any((subdependency) =>
         tryUnlockDepender(subdependency, seen));
   }
 
   List<PackageId> buildResults() {
-    return _packages.values.filter((dep) => dep.isDependedOn).map((dep) {
+    return _packages.values.where((dep) => dep.isDependedOn).mappedBy((dep) {
       var description = dep.description;
 
       // If the lockfile contains a fully-resolved description for the package,
@@ -208,7 +209,8 @@ class VersionSolver {
       }
 
       return new PackageId(dep.name, dep.source, dep.version, description);
-    });
+    })
+    .toList();
   }
 }
 
@@ -254,7 +256,7 @@ class ChangeVersion implements WorkItem {
     // them both and update any constraints that differ between the two.
     return Futures.wait([
         getDependencyRefs(solver, oldVersion),
-        getDependencyRefs(solver, version)]).transform((list) {
+        getDependencyRefs(solver, version)]).then((list) {
       var oldDependencyRefs = list[0];
       var newDependencyRefs = list[1];
 
@@ -289,7 +291,7 @@ class ChangeVersion implements WorkItem {
     }
 
     var id = new PackageId(package, source, version, description);
-    return solver._pubspecs.load(id).transform((pubspec) {
+    return solver._pubspecs.load(id).then((pubspec) {
       var dependencies = <String, PackageRef>{};
       for (var dependency in pubspec.dependencies) {
         dependencies[dependency.name] = dependency;
@@ -364,7 +366,7 @@ abstract class ChangeConstraint implements WorkItem {
 
     // The constraint has changed, so see what the best version of the package
     // that meets the new constraint is.
-    return solver.getBestVersion(newDependency).transform((best) {
+    return solver.getBestVersion(newDependency).then((best) {
       if (best == null) {
         undo(solver);
       } else if (newDependency.version != best) {
@@ -438,7 +440,7 @@ class UnlockPackage implements WorkItem {
     log.fine("Unlocking ${package.name}.");
 
     solver.lockFile.packages.remove(package.name);
-    return solver.getBestVersion(package).transform((best) {
+    return solver.getBestVersion(package).then((best) {
       if (best == null) return null;
       solver.enqueue(new ChangeVersion(
           package.name, package.source, package.description, best));
@@ -470,7 +472,7 @@ class PubspecCache {
       return new Future<Pubspec>.immediate(_pubspecs[id]);
     }
 
-    return id.describe().transform((pubspec) {
+    return id.describe().then((pubspec) {
       // Cache it.
       _pubspecs[id] = pubspec;
       return pubspec;
@@ -500,7 +502,7 @@ class Dependency {
   bool get isDependedOn => !_refs.isEmpty;
 
   /// The names of all the packages that depend on this dependency.
-  Collection<String> get dependers => _refs.keys;
+  Iterable<String> get dependers => _refs.keys;
 
   /// Gets the overall constraint that all packages are placing on this one.
   /// If no packages have a constraint on this one (which can happen when this
@@ -508,7 +510,7 @@ class Dependency {
   VersionConstraint get constraint {
     if (_refs.isEmpty) return null;
     return new VersionConstraint.intersection(
-        _refs.values.map((ref) => ref.constraint));
+        _refs.values.mappedBy((ref) => ref.constraint));
   }
 
   /// The source of this dependency's package.
@@ -535,7 +537,7 @@ class Dependency {
     for (var ref in refs) {
       if (ref is RootSource) return ref;
     }
-    return refs[0];
+    return refs.first;
   }
 
   Dependency(this.name)
@@ -576,7 +578,7 @@ class Dependency {
   String _requiredDepender() {
     if (_refs.isEmpty) return null;
 
-    var dependers = _refs.keys;
+    var dependers = _refs.keys.toList();
     if (dependers.length == 1) {
       var depender = dependers[0];
       if (_refs[depender].source is RootSource) return null;
@@ -700,8 +702,8 @@ class DescriptionMismatchException implements Exception {
     // TODO(nweiz): Dump descriptions to YAML when that's supported.
     return "Incompatible dependencies on '$package':\n"
         "- '$depender1' depends on it with description "
-        "${JSON.stringify(description1)}\n"
+        "${json.stringify(description1)}\n"
         "- '$depender2' depends on it with description "
-        "${JSON.stringify(description2)}";
+        "${json.stringify(description2)}";
   }
 }
diff --git a/lib/src/yaml/composer.dart b/lib/src/yaml/composer.dart
index 79aa2741..e9c18796 100644
--- a/lib/src/yaml/composer.dart
+++ b/lib/src/yaml/composer.dart
@@ -115,7 +115,7 @@ class _Composer extends _Visitor {
     var match = new RegExp("^[-+]?[0-9]+\$").firstMatch(content);
     if (match != null) {
       return new _ScalarNode(_Tag.yaml("int"),
-          value: Math.parseInt(match.group(0)));
+          value: int.parse(match.group(0)));
     }
 
     match = new RegExp("^0o([0-7]+)\$").firstMatch(content);
@@ -132,7 +132,7 @@ class _Composer extends _Visitor {
     match = new RegExp("^0x[0-9a-fA-F]+\$").firstMatch(content);
     if (match != null) {
       return new _ScalarNode(_Tag.yaml("int"),
-          value: Math.parseInt(match.group(0)));
+          value: int.parse(match.group(0)));
     }
 
     return null;
@@ -148,20 +148,20 @@ class _Composer extends _Visitor {
       // floats by removing the trailing dot.
       var matchStr = match.group(0).replaceAll(new RegExp(r"\.$"), "");
       return new _ScalarNode(_Tag.yaml("float"),
-          value: Math.parseDouble(matchStr));
+          value: double.parse(matchStr));
     }
 
     match = new RegExp("^([+-]?)\.(inf|Inf|INF)\$").firstMatch(content);
     if (match != null) {
       var infinityStr = match.group(1) == "-" ? "-Infinity" : "Infinity";
       return new _ScalarNode(_Tag.yaml("float"),
-          value: Math.parseDouble(infinityStr));
+          value: double.parse(infinityStr));
     }
 
     match = new RegExp("^\.(nan|NaN|NAN)\$").firstMatch(content);
     if (match != null) {
       return new _ScalarNode(_Tag.yaml("float"),
-          value: Math.parseDouble("NaN"));
+          value: double.parse("NaN"));
     }
 
     return null;
diff --git a/lib/src/yaml/model.dart b/lib/src/yaml/model.dart
index 57bf202b..8c111afe 100644
--- a/lib/src/yaml/model.dart
+++ b/lib/src/yaml/model.dart
@@ -90,7 +90,8 @@ class _SequenceNode extends _Node {
     return true;
   }
 
-  String toString() => '$tag [${Strings.join(content.map((e) => '$e'), ', ')}]';
+  String toString() =>
+      '$tag [${Strings.join(content.mappedBy((e) => '$e'), ', ')}]';
 
   int get hashCode => super.hashCode ^ _hashCode(content);
 
@@ -149,7 +150,7 @@ class _ScalarNode extends _Node {
       // TODO(nweiz): This could be faster if we used a RegExp to check for
       // special characters and short-circuited if they didn't exist.
 
-      var escapedValue = value.charCodes.map((c) {
+      var escapedValue = value.charCodes.mappedBy((c) {
         switch (c) {
         case _Parser.TAB: return "\\t";
         case _Parser.LF: return "\\n";
@@ -221,8 +222,9 @@ class _MappingNode extends _Node {
   }
 
   String toString() {
-    var strContent = Strings.join(content.keys.
-        map((k) => '${k}: ${content[k]}'), ', ');
+    var strContent = content.keys
+        .mappedBy((k) => '${k}: ${content[k]}')
+        .join(', ');
     return '$tag {$strContent}';
   }
 
diff --git a/lib/src/yaml/parser.dart b/lib/src/yaml/parser.dart
index 8652ff45..f2f255e7 100644
--- a/lib/src/yaml/parser.dart
+++ b/lib/src/yaml/parser.dart
@@ -633,7 +633,7 @@ class _Parser {
     if (!captureAs('', () => consumeChar(char))) return false;
     var captured = captureAndTransform(
         () => nAtOnce(digits, (c, _) => isHexDigit(c)),
-        (hex) => new String.fromCharCodes([Math.parseInt("0x$hex")]));
+        (hex) => new String.fromCharCodes([int.parse("0x$hex")]));
     return expect(captured, "$digits hexidecimal digits");
   }
 
diff --git a/lib/src/yaml/visitor.dart b/lib/src/yaml/visitor.dart
index 1d6282ae..b5c14c94 100644
--- a/lib/src/yaml/visitor.dart
+++ b/lib/src/yaml/visitor.dart
@@ -13,7 +13,8 @@ class _Visitor {
   visitScalar(_ScalarNode scalar) => scalar;
 
   /// Visits each node in [seq] and returns a list of the results.
-  visitSequence(_SequenceNode seq) => seq.content.map((e) => e.visit(this));
+  visitSequence(_SequenceNode seq)
+      => seq.content.mappedBy((e) => e.visit(this)).toList();
 
   /// Visits each key and value in [map] and returns a map of the results.
   visitMapping(_MappingNode map) {
diff --git a/lib/src/yaml/yaml.dart b/lib/src/yaml/yaml.dart
index 11984c34..6ce890cc 100644
--- a/lib/src/yaml/yaml.dart
+++ b/lib/src/yaml/yaml.dart
@@ -39,8 +39,9 @@ loadYaml(String yaml) {
 /// are YamlMaps. These have a few small behavioral differences from the default
 /// Map implementation; for details, see the YamlMap class.
 List loadYamlStream(String yaml) {
-  return new _Parser(yaml).l_yamlStream().map((doc) =>
-      new _Constructor(new _Composer(doc).compose()).construct());
+  return new _Parser(yaml).l_yamlStream().mappedBy((doc) =>
+      new _Constructor(new _Composer(doc).compose()).construct())
+      .toList();
 }
 
 /// An error thrown by the YAML processor.
diff --git a/lib/src/yaml/yaml_map.dart b/lib/src/yaml/yaml_map.dart
index 285027ce..65dd1199 100644
--- a/lib/src/yaml/yaml_map.dart
+++ b/lib/src/yaml/yaml_map.dart
@@ -29,8 +29,8 @@ class YamlMap implements Map {
   void clear() => _map.clear();
   void forEach(void f(key, value)) =>
     _map.forEach((k, v) => f(_unwrapKey(k), v));
-  Collection get keys => _map.keys.map(_unwrapKey);
-  Collection get values => _map.values;
+  Iterable get keys => _map.keys.mappedBy(_unwrapKey);
+  Iterable get values => _map.values;
   int get length => _map.length;
   bool get isEmpty => _map.isEmpty;
   String toString() => _map.toString();
@@ -81,7 +81,7 @@ class _WrappedHashKey {
 int _hashCode(obj, [List parents]) {
   if (parents == null) {
     parents = [];
-  } else if (parents.some((p) => identical(p, obj))) {
+  } else if (parents.any((p) => identical(p, obj))) {
     return -1;
   }
 
@@ -94,7 +94,7 @@ int _hashCode(obj, [List parents]) {
       return _hashCode(obj.keys, parents) ^
         _hashCode(obj.values, parents);
     }
-    if (obj is List) {
+    if (obj is Iterable) {
       // This is probably a really bad hash function, but presumably we'll get
       // this in the standard library before it actually matters.
       int hash = 0;
diff --git a/test/curl_client_test.dart b/test/curl_client_test.dart
index c040c2db..c2f6fb3f 100644
--- a/test/curl_client_test.dart
+++ b/test/curl_client_test.dart
@@ -215,7 +215,7 @@ void main() {
   tearDown(stopServer);
 
   test('head', () {
-    expect(new CurlClient().head(serverUrl).transform((response) {
+    expect(new CurlClient().head(serverUrl).then((response) {
       expect(response.statusCode, equals(200));
       expect(response.body, equals(''));
     }), completes);
@@ -225,7 +225,7 @@ void main() {
     expect(new CurlClient().get(serverUrl, headers: {
       'X-Random-Header': 'Value',
       'X-Other-Header': 'Other Value'
-    }).transform((response) {
+    }).then((response) {
       expect(response.statusCode, equals(200));
       expect(response.body, parse(equals({
         'method': 'GET',
@@ -245,7 +245,7 @@ void main() {
     }, fields: {
       'some-field': 'value',
       'other-field': 'other value'
-    }).transform((response) {
+    }).then((response) {
       expect(response.statusCode, equals(200));
       expect(response.body, parse(equals({
         'method': 'POST',
@@ -268,7 +268,7 @@ void main() {
       'X-Random-Header': 'Value',
       'X-Other-Header': 'Other Value',
       'Content-Type': 'text/plain'
-    }).transform((response) {
+    }).then((response) {
       expect(response.statusCode, equals(200));
       expect(response.body, parse(equals({
         'method': 'POST',
@@ -289,7 +289,7 @@ void main() {
     }, fields: {
       'some-field': 'value',
       'other-field': 'other value'
-    }).transform((response) {
+    }).then((response) {
       expect(response.statusCode, equals(200));
       expect(response.body, parse(equals({
         'method': 'PUT',
@@ -312,7 +312,7 @@ void main() {
       'X-Random-Header': 'Value',
       'X-Other-Header': 'Other Value',
       'Content-Type': 'text/plain'
-    }).transform((response) {
+    }).then((response) {
       expect(response.statusCode, equals(200));
       expect(response.body, parse(equals({
         'method': 'PUT',
@@ -330,7 +330,7 @@ void main() {
     expect(new CurlClient().delete(serverUrl, headers: {
       'X-Random-Header': 'Value',
       'X-Other-Header': 'Other Value'
-    }).transform((response) {
+    }).then((response) {
       expect(response.statusCode, equals(200));
       expect(response.body, parse(equals({
         'method': 'DELETE',
@@ -366,7 +366,7 @@ void main() {
     var future = new CurlClient().readBytes(serverUrl, headers: {
       'X-Random-Header': 'Value',
       'X-Other-Header': 'Other Value'
-    }).transform((bytes) => new String.fromCharCodes(bytes));
+    }).then((bytes) => new String.fromCharCodes(bytes));
 
     expect(future, completion(parse(equals({
       'method': 'GET',
@@ -389,11 +389,11 @@ void main() {
     request.headers[HttpHeaders.CONTENT_TYPE] =
       'application/json; charset=utf-8';
 
-    var future = client.send(request).chain((response) {
+    var future = client.send(request).then((response) {
       expect(response.statusCode, equals(200));
       return consumeInputStream(response.stream);
-    }).transform((bytes) => new String.fromCharCodes(bytes));
-    future.onComplete((_) => client.close());
+    }).then((bytes) => new String.fromCharCodes(bytes));
+    future.catchError((_) {}).then((_) => client.close());
 
     expect(future, completion(parse(equals({
       'method': 'POST',
@@ -411,7 +411,7 @@ void main() {
 
   test('with one redirect', () {
     var url = serverUrl.resolve('/redirect');
-    expect(new CurlClient().get(url).transform((response) {
+    expect(new CurlClient().get(url).then((response) {
       expect(response.statusCode, equals(200));
       expect(response.body, parse(equals({
         'method': 'GET',
@@ -433,7 +433,7 @@ void main() {
 
   test('with one redirect via HEAD', () {
     var url = serverUrl.resolve('/redirect');
-    expect(new CurlClient().head(url).transform((response) {
+    expect(new CurlClient().head(url).then((response) {
       expect(response.statusCode, equals(200));
     }), completes);
   });
@@ -451,8 +451,8 @@ void main() {
   test('without following redirects', () {
     var request = new http.Request('GET', serverUrl.resolve('/redirect'));
     request.followRedirects = false;
-    expect(new CurlClient().send(request).chain(http.Response.fromStream)
-        .transform((response) {
+    expect(new CurlClient().send(request).then(http.Response.fromStream)
+        .then((response) {
       expect(response.statusCode, equals(302));
       expect(response.isRedirect, true);
     }), completes);
diff --git a/test/oauth2_test.dart b/test/oauth2_test.dart
index f3d38e04..d8f3b34a 100644
--- a/test/oauth2_test.dart
+++ b/test/oauth2_test.dart
@@ -68,7 +68,7 @@ main() {
     confirmPublish(pub);
 
     server.handle('POST', '/token', (request, response) {
-      return consumeInputStream(request.inputStream).transform((bytes) {
+      return consumeInputStream(request.inputStream).then((bytes) {
         var body = new String.fromCharCodes(bytes);
         expect(body, matches(
             new RegExp(r'(^|&)refresh_token=refresh\+token(&|$)')));
@@ -199,7 +199,7 @@ void authorizePub(ScheduledProcess pub, ScheduledServer server,
     redirectUrl = addQueryParameters(redirectUrl, {'code': 'access code'});
     return (new http.Request('GET', redirectUrl)..followRedirects = false)
       .send();
-  }).transform((response) {
+  }).then((response) {
     expect(response.headers['location'],
         equals(['http://pub.dartlang.org/authorized']));
   }), anything);
@@ -209,7 +209,7 @@ void authorizePub(ScheduledProcess pub, ScheduledServer server,
 
 void handleAccessTokenRequest(ScheduledServer server, String accessToken) {
   server.handle('POST', '/token', (request, response) {
-    return consumeInputStream(request.inputStream).transform((bytes) {
+    return consumeInputStream(request.inputStream).then((bytes) {
       var body = new String.fromCharCodes(bytes);
       expect(body, matches(new RegExp(r'(^|&)code=access\+code(&|$)')));
 
diff --git a/test/pub_lish_test.dart b/test/pub_lish_test.dart
index 69555784..5319c466 100644
--- a/test/pub_lish_test.dart
+++ b/test/pub_lish_test.dart
@@ -13,7 +13,7 @@ import '../../pub/io.dart';
 
 void handleUploadForm(ScheduledServer server, [Map body]) {
   server.handle('GET', '/packages/versions/new.json', (request, response) {
-    return server.url.transform((url) {
+    return server.url.then((url) {
       expect(request.headers.value('authorization'),
           equals('Bearer access token'));
 
@@ -38,7 +38,7 @@ void handleUpload(ScheduledServer server) {
   server.handle('POST', '/upload', (request, response) {
     // TODO(nweiz): Once a multipart/form-data parser in Dart exists, validate
     // that the request body is correctly formatted. See issue 6952.
-    return server.url.transform((url) {
+    return server.url.then((url) {
       response.statusCode = 302;
       response.headers.set('location', url.resolve('/create').toString());
       response.outputStream.close();
diff --git a/test/test_pub.dart b/test/test_pub.dart
index 3da8fce9..a6042fb8 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -8,9 +8,9 @@
 /// tests like that.
 library test_pub;
 
+import 'dart:async';
 import 'dart:io';
-import 'dart:isolate';
-import 'dart:json';
+import 'dart:json' as json;
 import 'dart:math';
 import 'dart:uri';
 
@@ -80,7 +80,7 @@ void serve([List<Descriptor> contents]) {
   var baseDir = dir("serve-dir", contents);
 
   _schedule((_) {
-    return _closeServer().transform((_) {
+    return _closeServer().then((_) {
       _server = new HttpServer();
       _server.defaultRequestHandler = (request, response) {
         var path = request.uri.replaceFirst("/", "").split("/");
@@ -101,9 +101,7 @@ void serve([List<Descriptor> contents]) {
           response.contentLength = data.length;
           response.outputStream.write(data);
           response.outputStream.close();
-        });
-
-        future.handleException((e) {
+        }).catchError((e) {
           print("Exception while handling ${request.uri}: $e");
           response.statusCode = 500;
           response.reasonPhrase = e.message;
@@ -161,7 +159,7 @@ void servePackages(List<Map> pubspecs) {
   }
 
   _schedule((_) {
-    return _awaitObject(pubspecs).transform((resolvedPubspecs) {
+    return _awaitObject(pubspecs).then((resolvedPubspecs) {
       for (var spec in resolvedPubspecs) {
         var name = spec['name'];
         var version = spec['version'];
@@ -172,12 +170,12 @@ void servePackages(List<Map> pubspecs) {
 
       _servedPackageDir.contents.clear();
       for (var name in _servedPackages.keys) {
-        var versions = _servedPackages[name].keys;
+        var versions = _servedPackages[name].keys.toList());
         _servedPackageDir.contents.addAll([
           file('$name.json',
-              JSON.stringify({'versions': versions})),
+              json.stringify({'versions': versions})),
           dir(name, [
-            dir('versions', flatten(versions.map((version) {
+            dir('versions', flatten(versions.mappedBy((version) {
               return [
                 file('$version.yaml', _servedPackages[name][version]),
                 tar('$version.tar.gz', [
@@ -194,7 +192,7 @@ void servePackages(List<Map> pubspecs) {
 }
 
 /// Converts [value] into a YAML string.
-String yaml(value) => JSON.stringify(value);
+String yaml(value) => json.stringify(value);
 
 /// Describes a package that passes all validation.
 Descriptor get normalPackage => dir(appPath, [
@@ -211,7 +209,7 @@ Descriptor get normalPackage => dir(appPath, [
 /// [contents] may contain [Future]s that resolve to serializable objects,
 /// which may in turn contain [Future]s recursively.
 Descriptor pubspec(Map contents) {
-  return async(_awaitObject(contents).transform((resolvedContents) =>
+  return async(_awaitObject(contents).then((resolvedContents) =>
       file("pubspec.yaml", yaml(resolvedContents))));
 }
 
@@ -261,7 +259,7 @@ Map package(String name, String version, [List dependencies]) {
 /// Describes a map representing a dependency on a package in the package
 /// repository.
 Map dependency(String name, [String versionConstraint]) {
-  var url = port.transform((p) => "http://localhost:$p");
+  var url = port.then((p) => "http://localhost:$p");
   var dependency = {"hosted": {"name": name, "url": url}};
   if (versionConstraint != null) dependency["version"] = versionConstraint;
   return dependency;
@@ -331,7 +329,7 @@ DirectoryDescriptor cacheDir(Map packages) {
   });
   return dir(cachePath, [
     dir('hosted', [
-      async(port.transform((p) => dir('localhost%58$p', contents)))
+      async(port.then((p) => dir('localhost%58$p', contents)))
     ])
   ]);
 }
@@ -344,7 +342,7 @@ Descriptor credentialsFile(
     String accessToken,
     {String refreshToken,
      Date expiration}) {
-  return async(server.url.transform((url) {
+  return async(server.url.then((url) {
     return dir(cachePath, [
       file('credentials.json', new oauth2.Credentials(
           accessToken,
@@ -364,10 +362,10 @@ DirectoryDescriptor appDir(List dependencies) =>
 /// Converts a list of dependencies as passed to [package] into a hash as used
 /// in a pubspec.
 Future<Map> _dependencyListToMap(List<Map> dependencies) {
-  return _awaitObject(dependencies).transform((resolvedDependencies) {
+  return _awaitObject(dependencies).then((resolvedDependencies) {
     var result = <String, Map>{};
     for (var dependency in resolvedDependencies) {
-      var keys = dependency.keys.filter((key) => key != "version");
+      var keys = dependency.keys.where((key) => key != "version");
       var sourceName = only(keys);
       var source;
       switch (sourceName) {
@@ -453,7 +451,7 @@ void run() {
   var asyncDone = expectAsync0(() {});
 
   Future cleanup() {
-    return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) {
+    return _runScheduled(createdSandboxDir, _scheduledCleanup).then((_) {
       _scheduled = null;
       _scheduledCleanup = null;
       _scheduledOnException = null;
@@ -462,29 +460,25 @@ void run() {
     });
   }
 
-  final future = _setUpSandbox().chain((sandboxDir) {
+  final future = _setUpSandbox().then((sandboxDir) {
     createdSandboxDir = sandboxDir;
     return _runScheduled(sandboxDir, _scheduled);
   });
 
-  future.handleException((error) {
+  future.catchError((error) {
     // If an error occurs during testing, delete the sandbox, throw the error so
     // that the test framework sees it, then finally call asyncDone so that the
     // test framework knows we're done doing asynchronous stuff.
     var subFuture = _runScheduled(createdSandboxDir, _scheduledOnException)
-        .chain((_) => cleanup());
-    subFuture.handleException((e) {
-      print("Exception while cleaning up: $e");
-      print(subFuture.stackTrace);
-      registerException(error, subFuture.stackTrace);
+        .then((_) => cleanup());
+    subFuture.catchError((e) {
+      print("Exception while cleaning up: ${e.error}");
+      print(e.stackTrace);
+      registerException(e.error, e.stackTrace);
       return true;
     });
-    subFuture.then((_) => registerException(error, future.stackTrace));
-    return true;
-  });
-
   timeout(future, _TIMEOUT, 'waiting for a test to complete')
-      .chain((_) => cleanup())
+      .then((_) => cleanup())
       .then((_) => asyncDone());
 }
 
@@ -503,7 +497,7 @@ void schedulePub({List args, Pattern output, Pattern error,
     Future<Uri> tokenEndpoint, int exitCode: 0}) {
   _schedule((sandboxDir) {
     return _doPub(runProcess, sandboxDir, args, tokenEndpoint)
-        .transform((result) {
+        .then((result) {
       var failures = [];
 
       _validateOutput(failures, 'stdout', output, result.stdout);
@@ -518,7 +512,7 @@ void schedulePub({List args, Pattern output, Pattern error,
         if (error == null) {
           // If we aren't validating the error, still show it on failure.
           failures.add('Pub stderr:');
-          failures.addAll(result.stderr.map((line) => '| $line'));
+          failures.addAll(result.stderr.mappedBy((line) => '| $line'));
         }
 
         throw new ExpectException(Strings.join(failures, '\n'));
@@ -629,7 +623,7 @@ Future _doPub(Function fn, sandboxDir, List args, Future<Uri> tokenEndpoint) {
 /// about the pub git tests).
 void ensureGit() {
   _schedule((_) {
-    return isGitInstalled.transform((installed) {
+    return isGitInstalled.then((installed) {
       if (!installed &&
           !Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) {
         _abortScheduled = true;
@@ -655,18 +649,18 @@ Future<Directory> _setUpSandbox() => createTempDir();
 
 Future _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) {
   if (scheduled == null) return new Future.immediate(null);
-  var iterator = scheduled.iterator();
+  var iterator = scheduled.iterator;
 
   Future runNextEvent(_) {
-    if (_abortScheduled || !iterator.hasNext) {
+    if (_abortScheduled || !iterator.moveNext()) {
       _abortScheduled = false;
       scheduled.clear();
       return new Future.immediate(null);
     }
 
-    var future = iterator.next()(parentDir);
+    var future = iterator.current(parentDir);
     if (future != null) {
-      return future.chain(runNextEvent);
+      return future.then(runNextEvent);
     } else {
       return runNextEvent(null);
     }
@@ -699,7 +693,7 @@ void _validateOutputRegex(List<String> failures, String pipe,
     failures.add('Expected $pipe to match "${expected.pattern}" but got none.');
   } else {
     failures.add('Expected $pipe to match "${expected.pattern}" but got:');
-    failures.addAll(actual.map((line) => '| $line'));
+    failures.addAll(actual.mappedBy((line) => '| $line'));
   }
 }
 
@@ -744,7 +738,7 @@ void _validateOutputString(List<String> failures, String pipe,
   // If any lines mismatched, show the expected and actual.
   if (failed) {
     failures.add('Expected $pipe:');
-    failures.addAll(expected.map((line) => '| $line'));
+    failures.addAll(expected.mappedBy((line) => '| $line'));
     failures.add('Got:');
     failures.addAll(results);
   }
@@ -802,7 +796,7 @@ abstract class Descriptor {
     // Special-case strings to support multi-level names like "myapp/packages".
     if (name is String) {
       var path = join(dir, name);
-      return exists(path).chain((exists) {
+      return exists(path).then((exists) {
         if (!exists) Expect.fail('File $name in $dir not found.');
         return validate(path);
       });
@@ -816,9 +810,9 @@ abstract class Descriptor {
       stackTrace = localStackTrace;
     }
 
-    return listDir(dir).chain((files) {
-      var matches = files.filter((file) => endsWithPattern(file, name));
-      if (matches.length == 0) {
+    return listDir(dir).then((files) {
+      var matches = files.where((file) => endsWithPattern(file, name)).toList();
+      if (matches.isEmpty) {
         Expect.fail('No files in $dir match pattern $name.');
       }
       if (matches.length == 1) return validate(matches[0]);
@@ -845,16 +839,15 @@ abstract class Descriptor {
       for (var match in matches) {
         var future = validate(match);
 
-        future.handleException((e) {
+        future.catchError((e) {
           failures.add(e);
           checkComplete();
-          return true;
         });
 
         future.then((_) {
           successes++;
           checkComplete();
-        });
+        }).catchError(() {});
       }
       return completer.future;
     });
@@ -885,7 +878,7 @@ class FileDescriptor extends Descriptor {
   /// Validates that this file correctly matches the actual file at [path].
   Future validate(String path) {
     return _validateOneMatch(path, (file) {
-      return readTextFile(file).transform((text) {
+      return readTextFile(file).then((text) {
         if (text == contents) return null;
 
         Expect.fail('File $file should contain:\n\n$contents\n\n'
@@ -923,13 +916,14 @@ class DirectoryDescriptor extends Descriptor {
   /// the creation is done.
   Future<Directory> create(parentDir) {
     // Create the directory.
-    return ensureDir(join(parentDir, _stringName)).chain((dir) {
+    return ensureDir(join(parentDir, _stringName)).then((dir) {
       if (contents == null) return new Future<Directory>.immediate(dir);
 
       // Recursively create all of its children.
-      final childFutures = contents.map((child) => child.create(dir));
+      final childFutures =
+          contents.mappedBy((child) => child.create(dir)).toList();
       // Only complete once all of the children have been created too.
-      return Futures.wait(childFutures).transform((_) => dir);
+      return Futures.wait(childFutures).then((_) => dir);
     });
   }
 
@@ -946,10 +940,11 @@ class DirectoryDescriptor extends Descriptor {
   Future validate(String path) {
     return _validateOneMatch(path, (dir) {
       // Validate each of the items in this directory.
-      final entryFutures = contents.map((entry) => entry.validate(dir));
+      final entryFutures =
+          contents.mappedBy((entry) => entry.validate(dir)).toList();
 
       // If they are all valid, the directory is valid.
-      return Futures.wait(entryFutures).transform((entries) => null);
+      return Futures.wait(entryFutures).then((entries) => null);
     });
   }
 
@@ -978,11 +973,11 @@ class FutureDescriptor extends Descriptor {
 
   FutureDescriptor(this._future) : super('<unknown>');
 
-  Future create(dir) => _future.chain((desc) => desc.create(dir));
+  Future create(dir) => _future.then((desc) => desc.create(dir));
 
-  Future validate(dir) => _future.chain((desc) => desc.validate(dir));
+  Future validate(dir) => _future.then((desc) => desc.validate(dir));
 
-  Future delete(dir) => _future.chain((desc) => desc.delete(dir));
+  Future delete(dir) => _future.then((desc) => desc.delete(dir));
 
   InputStream load(List<String> path) {
     var resultStream = new ListInputStream();
@@ -1020,9 +1015,9 @@ class GitRepoDescriptor extends DirectoryDescriptor {
   /// referred to by [ref] at the current point in the scheduled test run.
   Future<String> revParse(String ref) {
     return _scheduleValue((parentDir) {
-      return super.create(parentDir).chain((rootDir) {
+      return super.create(parentDir).then((rootDir) {
         return _runGit(['rev-parse', ref], rootDir);
-      }).transform((output) => output[0]);
+      }).then((output) => output[0]);
     });
   }
 
@@ -1040,10 +1035,10 @@ class GitRepoDescriptor extends DirectoryDescriptor {
     Future runGitStep(_) {
       if (commands.isEmpty) return new Future.immediate(workingDir);
       var command = commands.removeAt(0);
-      return _runGit(command, workingDir).chain(runGitStep);
+      return _runGit(command, workingDir).then(runGitStep);
     }
 
-    return super.create(parentDir).chain((rootDir) {
+    return super.create(parentDir).then((rootDir) {
       workingDir = rootDir;
       return runGitStep(null);
     });
@@ -1060,7 +1055,7 @@ class GitRepoDescriptor extends DirectoryDescriptor {
     };
 
     return runGit(args, workingDir: workingDir.path,
-        environment: environment).transform((result) {
+        environment: environment).then((result) {
       if (!result.success) {
         throw "Error running: git ${Strings.join(args, ' ')}\n"
             "${Strings.join(result.stderr, '\n')}";
@@ -1083,15 +1078,15 @@ class TarFileDescriptor extends Descriptor {
   Future<File> create(parentDir) {
     // TODO(rnystrom): Use withTempDir().
     var tempDir;
-    return createTempDir().chain((_tempDir) {
+    return createTempDir().then((_tempDir) {
       tempDir = _tempDir;
-      return Futures.wait(contents.map((child) => child.create(tempDir)));
-    }).chain((createdContents) {
+      return Futures.wait(contents.mappedBy((child) => child.create(tempDir)));
+    }).then((createdContents) {
       return consumeInputStream(createTarGz(createdContents, baseDir: tempDir));
-    }).chain((bytes) {
+    }).then((bytes) {
       return new File(join(parentDir, _stringName)).writeAsBytes(bytes);
-    }).chain((file) {
-      return deleteDir(tempDir).transform((_) => file);
+    }).then((file) {
+      return deleteDir(tempDir).then((_) => file);
     });
   }
 
@@ -1116,7 +1111,7 @@ class TarFileDescriptor extends Descriptor {
     var tempDir;
     // TODO(rnystrom): Use withTempDir() here.
     // TODO(nweiz): propagate any errors to the return value. See issue 3657.
-    createTempDir().chain((_tempDir) {
+    createTempDir().then((_tempDir) {
       tempDir = _tempDir;
       return create(tempDir);
     }).then((tar) {
@@ -1137,7 +1132,7 @@ class NothingDescriptor extends Descriptor {
   Future delete(dir) => new Future.immediate(null);
 
   Future validate(String dir) {
-    return exists(join(dir, name)).transform((exists) {
+    return exists(join(dir, name)).then((exists) {
       if (exists) Expect.fail('File $name in $dir should not exist.');
     });
   }
@@ -1167,7 +1162,7 @@ Future<Pair<List<String>, List<String>>> schedulePackageValidation(
     return Entrypoint.load(join(sandboxDir, appPath), cache)
         .chain((entrypoint) {
       var validator = fn(entrypoint);
-      return validator.validate().transform((_) {
+      return validator.validate().then((_) {
         return new Pair(validator.errors, validator.warnings);
       });
     });
@@ -1240,8 +1235,8 @@ class ScheduledProcess {
   /// Wraps a [Process] [Future] in a scheduled process.
   ScheduledProcess(this.name, Future<Process> process)
     : _process = process,
-      _stdout = process.transform((p) => new StringInputStream(p.stdout)),
-      _stderr = process.transform((p) => new StringInputStream(p.stderr)) {
+      _stdout = process.then((p) => new StringInputStream(p.stdout)),
+      _stderr = process.then((p) => new StringInputStream(p.stderr)) {
 
     _schedule((_) {
       if (!_endScheduled) {
@@ -1249,7 +1244,7 @@ class ScheduledProcess {
             "or kill() called before the test is run.");
       }
 
-      return _process.transform((p) {
+      return _process.then((p) {
         p.onExit = (c) {
           if (_endExpected) {
             _exitCodeCompleter.complete(c);
@@ -1343,7 +1338,7 @@ class ScheduledProcess {
 
   /// Writes [line] to the process as stdin.
   void writeLine(String line) {
-    _schedule((_) => _process.transform((p) => p.stdin.writeString('$line\n')));
+    _schedule((_) => _process.then((p) => p.stdin.writeString('$line\n')));
   }
 
   /// Kills the process, and waits until it's dead.
@@ -1366,7 +1361,7 @@ class ScheduledProcess {
     _schedule((_) {
       _endExpected = true;
       return timeout(_exitCode, _SCHEDULE_TIMEOUT,
-          "waiting for process $name to exit").transform((exitCode) {
+          "waiting for process $name to exit").then((exitCode) {
         if (expectedExitCode != null) {
           expect(exitCode, equals(expectedExitCode));
         }
@@ -1378,7 +1373,7 @@ class ScheduledProcess {
   /// Prints nothing if the straems are empty.
   Future _printStreams() {
     Future printStream(String streamName, StringInputStream stream) {
-      return consumeStringInputStream(stream).transform((output) {
+      return consumeStringInputStream(stream).then((output) {
         if (output.isEmpty) return;
 
         print('\nProcess $name $streamName:');
@@ -1424,11 +1419,11 @@ class ScheduledServer {
   }
 
   /// The port on which the server is listening.
-  Future<int> get port => _server.transform((s) => s.port);
+  Future<int> get port => _server.then((s) => s.port);
 
   /// The base URL of the server, including its port.
   Future<Uri> get url =>
-    port.transform((p) => new Uri.fromString("http://localhost:$p"));
+    port.then((p) => new Uri.fromString("http://localhost:$p"));
 
   /// Assert that the next request has the given [method] and [path], and pass
   /// it to [handler] to handle. If [handler] returns a [Future], wait until
@@ -1466,7 +1461,7 @@ class ScheduledServer {
         fail('Unexpected ${request.method} request to ${request.path}.');
       }
       return _handlers.removeFirst();
-    }).transform((handler) {
+    }).then((handler) {
       handler(request, response);
     }), _SCHEDULE_TIMEOUT, "waiting for a handler for ${request.method} "
         "${request.path}");
@@ -1479,16 +1474,18 @@ class ScheduledServer {
 /// Completes with the fully resolved structure.
 Future _awaitObject(object) {
   // Unroll nested futures.
-  if (object is Future) return object.chain(_awaitObject);
-  if (object is Collection) return Futures.wait(object.map(_awaitObject));
+  if (object is Future) return object.then(_awaitObject);
+  if (object is Collection) {
+    return Futures.wait(object.mappedBy(_awaitObject).toList());
+  }
   if (object is! Map) return new Future.immediate(object);
 
   var pairs = <Future<Pair>>[];
   object.forEach((key, value) {
     pairs.add(_awaitObject(value)
-        .transform((resolved) => new Pair(key, resolved)));
+        .then((resolved) => new Pair(key, resolved)));
   });
-  return Futures.wait(pairs).transform((resolvedPairs) {
+  return Futures.wait(pairs).then((resolvedPairs) {
     var map = {};
     for (var pair in resolvedPairs) {
       map[pair.first] = pair.last;
@@ -1537,7 +1534,7 @@ void _scheduleOnException(_ScheduledEvent event) {
 void expectLater(Future actual, matcher, {String reason,
     FailureHandler failureHandler, bool verbose: false}) {
   _schedule((_) {
-    return actual.transform((value) {
+    return actual.then((value) {
       expect(value, matcher, reason: reason, failureHandler: failureHandler,
           verbose: false);
     });
diff --git a/test/version_solver_test.dart b/test/version_solver_test.dart
index eb49d3d8..b5b30eda 100644
--- a/test/version_solver_test.dart
+++ b/test/version_solver_test.dart
@@ -4,8 +4,8 @@
 
 library pub_update_test;
 
+import 'dart:async';
 import 'dart:io';
-import 'dart:isolate';
 
 import '../../pub/lock_file.dart';
 import '../../pub/package.dart';
@@ -452,10 +452,9 @@ testResolve(description, packages, {lockfile, result, Matcher error}) {
 
     // If we aren't expecting an error, print some debugging info if we get one.
     if (error == null) {
-      future.handleException((ex) {
+      future.catchError((ex) {
         print(ex);
         print(future.stackTrace);
-        return true;
       });
     }
   });
@@ -478,7 +477,7 @@ class MockSource extends Source {
       : _packages = <String, Map<Version, Package>>{};
 
   Future<List<Version>> getVersions(String name, String description) {
-    return fakeAsync(() => _packages[description].keys);
+    return fakeAsync(() => _packages[description].keys.toList());
   }
 
   Future<Pubspec> describe(PackageId id) {
diff --git a/test/yaml_test.dart b/test/yaml_test.dart
index 85cbdf81..889923e5 100644
--- a/test/yaml_test.dart
+++ b/test/yaml_test.dart
@@ -4,8 +4,6 @@
 
 library yaml_test;
 
-import 'dart:math';
-
 import '../../../pkg/unittest/lib/unittest.dart';
 import '../../pub/yaml/yaml.dart';
 import '../../pub/yaml/deep_equals.dart';
@@ -32,8 +30,8 @@ expectYamlStreamLoads(List expected, String source) {
 }
 
 main() {
-  var infinity = parseDouble("Infinity");
-  var nan = parseDouble("NaN");
+  var infinity = double.parse("Infinity");
+  var nan = double.parse("NaN");
 
   group('YamlMap', () {
     group('accepts as a key', () {
-- 
GitLab