[sledge] Improve Sledge initialization.

Callbacks passed to FIDL bindings are executed with a different stack
than the one that initiated the call to the FIDL binding.
This means that you can't catch exceptions thrown from inside callbacks
executed by FIDL bindings.

With this CL, errors that happen during the initialization of Sledge do
not throw exceptions anymore.
Instead, the presence of error is only recorded in the
initializationCompleter Completer.

TEST=ran sledge_testing_mod

Change-Id: Ifb131d639185380254a4109468276bde2f732eb0
diff --git a/public/dart/sledge/lib/src/sledge.dart b/public/dart/sledge/lib/src/sledge.dart
index 661a33c..0d188af 100644
--- a/public/dart/sledge/lib/src/sledge.dart
+++ b/public/dart/sledge/lib/src/sledge.dart
@@ -18,7 +18,6 @@
 import 'modification_queue.dart';
 import 'query/query.dart';
 import 'sledge_connection_id.dart';
-import 'sledge_errors.dart';
 import 'sledge_page_id.dart';
 import 'storage/kv_encoding.dart' as sledge_storage;
 import 'subscription/subscription.dart';
@@ -61,25 +60,41 @@
         _ledgerObjectsFactory = new LedgerObjectsFactoryImpl() {
     pageId ??= new SledgePageId();
 
-    _ledgerProxy.ctrl.bind(ledgerHandle);
-
+    // The initialization sequence consists of:
+    // 1/ Obtaining a LedgerProxy from the LedgerHandle.
+    // 2/ Setting a conflict resolver on the LedgerProxy (not yet implemented).
+    // 3/ Obtaining a LedgerPageProxy using the LedgerProxy.
+    // 4/ Subscribing for change notifications on the LedgerPageProxy.
+    // Any of these steps can fail.
+    //
+    // The following Completer is completed with `false` if an error occurs at
+    // any step. It is completed with `true` if the 4th step finishes
+    // succesfully.
+    //
+    // Operations that require the succesfull initialization of the Sledge
+    // instance await the Future returned by this completer.
     Completer<bool> initializationCompleter = new Completer<bool>();
 
     _ledgerProxy.ctrl.onConnectionError = () {
-      initializationCompleter.complete(false);
-      throw new Exception('Sledge was disconnected from the Ledger.');
+      if (!initializationCompleter.isCompleted) {
+        initializationCompleter.complete(false);
+      }
     };
 
+    _ledgerProxy.ctrl.bind(ledgerHandle);
+
     _ledgerProxy.getPage(pageId.id, _pageProxy.ctrl.request(),
         (ledger.Status status) {
+      if (initializationCompleter.isCompleted) {
+        return;
+      }
       if (status != ledger.Status.ok) {
         initializationCompleter.complete(false);
-        throw new Exception('Sledge failed to GetPage with status `$status`.');
-      } else {
-        _modificationQueue =
-            new ModificationQueue(this, _ledgerObjectsFactory, _pageProxy);
-        _subscribe(initializationCompleter);
+        return;
       }
+      _modificationQueue =
+          new ModificationQueue(this, _ledgerObjectsFactory, _pageProxy);
+      _subscribe(initializationCompleter);
     });
 
     _initializationSucceeded = initializationCompleter.future;
@@ -209,10 +224,8 @@
 
   /// Subscribes for page.onChange to perform applyChange.
   Subscription _subscribe(Completer<bool> subscriptionCompleter) {
-    if (_modificationQueue.currentTransaction != null) {
-      throw new InternalSledgeError(
-          'Must be called before any transaction can start.');
-    }
+    assert(_modificationQueue.currentTransaction == null,
+        '`_subscribe` must be called before any transaction can start.');
     return new Subscription(
         _pageProxy, _ledgerObjectsFactory, _applyChange, subscriptionCompleter);
   }
diff --git a/public/dart/sledge/lib/src/subscription/subscription.dart b/public/dart/sledge/lib/src/subscription/subscription.dart
index 3910091..7a04192 100644
--- a/public/dart/sledge/lib/src/subscription/subscription.dart
+++ b/public/dart/sledge/lib/src/subscription/subscription.dart
@@ -34,10 +34,13 @@
     );
 
     completer.future.then((ledger.Status status) {
-      if (status != ledger.Status.ok) {
-        subscriptionCompleter.complete(false);
+      if (subscriptionCompleter.isCompleted) {
+        // If an error occurs, `subscriptionCompleter` may have been completed
+        // by the caller before `completer` has ran.
+        return;
       }
-      subscriptionCompleter.complete(true);
+      bool subscriptionSuccesfull = status == ledger.Status.ok;
+      subscriptionCompleter.complete(subscriptionSuccesfull);
     });
   }