anchor.diff:      notifyChange(new PropertyChangeRecord(prop));
     }
   
  +  void _load() {
  +    assert(!_loaded);
  +    _storage.getKeys()
  +      .then((List<String> keys) {
  +        return Future.forEach(keys, (String key) {
  +          return _storage.get(key)
  +              .then((Object json) {
  +                _set(key, json);
  +              });
  +        });
  +
  +      })
  +      .then((_) {
  +        assert(!_loaded);
  +        _loaded = true;
  +        _notifyChange(const Symbol('loaded'));
  +      });
  +  }
  +
     static const _UPDATED = const Symbol('updated');
   }
   
  
positive.code: //    No changes underneath
  // 2) Assume all values are json-able
  
  class MapSync<E> extends ChangeNotifierBase {
    final _Encoder<E, dynamic> _encode;
    final _Decoder<E, dynamic> _decode;
  
negative.code:   final Storage _storage;
  
    bool _syncActive = false;
    bool _loaded = false;
  
    MapSync(this._storage, [Codec<E, dynamic> codec]) :
      _encode = (codec == null) ? _identity : codec.encode,
      _decode = (codec == null) ? _identity : codec.decode {
  
      _map.onKeyChanged.listen(_onKeyDirty);
      _load();
    }
  
    //
    // Properties
    //
  
    bool get loaded => _loaded;
  
    bool get updated => _loaded && _dirtyKeys.isEmpty;
  
    Map<String, E> get map => _loaded ? _map : const {};
  
    //
    // Methods
    //
  
    static Future<MapSync> create(Storage storage, [Codec codec]) {
      var ms = new MapSync(storage, codec);
  
      return ms.changes
          .firstWhere((List<ChangeRecord> changes) {
              return changes
                .where((ChangeRecord cr) => cr is PropertyChangeRecord)
                .any((PropertyChangeRecord pcr) =>
                    pcr.field == const Symbol('loaded'));
          })
          .then((_) {
            assert(ms.loaded);
            return ms;
          });
    }
  